2

I'm encountering a problem creating a Castle DynamicProxy for a generic object with an overloaded method signature. I originally ran into the problem via Moq, but I was able to reproduce the same Exception by generating a fairly simple proxy manually:

using Castle.DynamicProxy;
using NUnit.Framework;

[TestFixture]
public class TestOverloadedMethods
{
    [Test]
    public void ProxyCreationFails()
    {
        var generator = new ProxyGenerator();
        generator.CreateClassProxy(typeof(SomeDependency<SomeParam>),
            ProxyGenerationOptions.Default, new object[] { });
    }
}

public abstract class SomeDependency<T>
{
    protected virtual void Do(T param) { }
    public virtual void Do(SomeParam param) { }
}

public class SomeParam { }

The proxy creation fails with the following Exception:

System.TypeLoadException : Derived method 'Do' in type 'Castle.Proxies.SomeDependency`1Proxy' 
from assembly 'DynamicProxyGenAssembly2' cannot reduce access.

   at System.Reflection.Emit.TypeBuilder.TermCreateClass(RuntimeModule module, Int32 tk, ObjectHandleOnStack type)
   at System.Reflection.Emit.TypeBuilder.CreateTypeNoLock()
   at System.Reflection.Emit.TypeBuilder.CreateTypeInfo()
   at Castle.DynamicProxy.Generators.Emitters.AbstractTypeEmitter.CreateType(TypeBuilder type)
   at Castle.DynamicProxy.Generators.Emitters.AbstractTypeEmitter.BuildType()
   at Castle.DynamicProxy.Generators.ClassProxyGenerator.GenerateType(String name, Type[] interfaces, INamingScope namingScope)
   at Castle.DynamicProxy.Generators.BaseProxyGenerator.ObtainProxyType(CacheKey cacheKey, Func`3 factory)
   at Castle.DynamicProxy.ProxyGenerator.CreateClassProxy(Type classToProxy, Type[] additionalInterfacesToProxy, ProxyGenerationOptions options, Object[] constructorArguments, IInterceptor[] interceptors)
   at Castle.DynamicProxy.ProxyGenerator.CreateClassProxy(Type classToProxy, ProxyGenerationOptions options, Object[] constructorArguments, IInterceptor[] interceptors)
   at TestAmbiguousMethods.ProxyCreationFails() in TestOverloadedMethods.cs:line 11

The following hacks fix the problem but rely on changes to the class definition:

  • Rename one of the methods, so they're no longer overloaded.
  • Reorder the code in the class, so that the public method is defined before the protected method.

Is there a way to create the proxy without modifying the code for the dependency?

Origameg
  • 324
  • 2
  • 9
  • I tried using Moq directly. When I do `myMock.Setup(x => x.Do(Moq.It.IsAny()))`, according to the C# overload resolution rules, I seem to be pointing to the `public` overload like I want to. If I inspect the expression tree directly, `System.Linq.Expressions.Expression>> expr = x => x.Do(Moq.It.IsAny());` the `Body` has a `Method` (which is a method info) whose `IsPublic` is true. So with the C# expression tree, I point to one specific overload, the public one. So Moq must not pick the other overload! Seen at this level, it is a bug. – Jeppe Stig Nielsen Jul 03 '17 at 11:14
  • 1
    Your class `SomeDependency` also gives problems if you try to write the mock be hand, in C#. If you do `class C : SomeDependency { }`, everything is still OK even though the two overloads ___unite___ (acquire same signature). The overload whose argument does not come from `T` seems to be preferred. But as soon as you try either `public override void Do(SomeParam param) { }` or `protected override void Do(SomeParam param) { }`, you are presented with: _error CS0462: The inherited members 'SomeDependency.Do(SomeParam)' and 'SomeDependency.Do(T)' have the same signature ..._ – Jeppe Stig Nielsen Jul 03 '17 at 11:54
  • Thanks for noticing that! The creator of SomeDependency had only made the methods virtual in order to allow them to be manipulated during unit tests. Since there was never a real-life override, it wasn't obvious that it could be considered invalid. I will ask for the issue to be addressed by the dependency producers. – Origameg Jul 04 '17 at 09:04
  • It could be that the runtime/CLI offers no way to specify, in CIL byte code, which method is being overridden, when two methods _unite_ like this. If this is the case, the "bug" in Castle DynamicProxy can never be fixed, unless the limitation of .NET is somehow removed. _Note:_ The problem occurs even if only one of the two overloads in `SomeDependency` is marked `virtual`, the other being non-virtual. – Jeppe Stig Nielsen Jul 04 '17 at 09:20

0 Answers0