Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CreateClassProxy: Unable to specify ctor to use when passing ambiguous ctor args #480

Open
stakx opened this issue Feb 17, 2020 · 1 comment

Comments

@stakx
Copy link
Member

stakx commented Feb 17, 2020

Somewhat of an edge case perhaps, but we just got a bug report over at Moq that essentially boils down to this:

using Castle.DynamicProxy;

public class Foo
{
    public Foo(A a) { }
    public Foo(B b) { }
}

public class A { }
public class B { }

var generator = new ProxyGenerator();
var proxy = (Foo)generator.CreateClassProxy(typeof(Foo), new object[] { default(A) }, new StandardInterceptor());

which throws the following exception:

System.Reflection.AmbiguousMatchException: Ambiguous match found.
   at System.DefaultBinder.BindToMethod(BindingFlags bindingAttr, MethodBase[] match, Object[]& args, ParameterModifier[] modifiers, CultureInfo cultureInfo, String[] names, Object& state)
   at System.RuntimeType.CreateInstanceImpl(BindingFlags bindingAttr, Binder binder, Object[] args, CultureInfo culture, Object[] activationAttributes, StackCrawlMark& stackMark)
   at System.Activator.CreateInstance(Type type, BindingFlags bindingAttr, Binder binder, Object[] args, CultureInfo culture, Object[] activationAttributes)
   at System.Activator.CreateInstance(Type type, Object[] args)
   at Castle.DynamicProxy.ProxyGenerator.CreateClassProxyInstance(Type proxyType, List`1 proxyArguments, Type classToProxy, Object[] constructorArguments)
   at Castle.DynamicProxy.ProxyGenerator.CreateClassProxy(Type classToProxy, Type[] additionalInterfacesToProxy, ProxyGenerationOptions options, Object[] constructorArguments, IInterceptor[] interceptors)
   at Castle.DynamicProxy.ProxyGenerator.CreateClassProxy(Type classToProxy, Object[] constructorArguments, IInterceptor[] interceptors)
   at Program.Main()

The reason is quite clear, the ctor argument default(A) is equivalent to null, and that null is being put in a object[] array, so all type information is lost and it's impossible for Reflection to decide whether Foo..ctor(A) or Foo..ctor(B) was intended to be called.

The only way around this problem would be to enhance DynamicProxy's API such that either (a) the targeted base ctor could be specified in the form of a ConstructorInfo; or (b) the ctor argument types could get passed in a separate Type[].

@r-pankevicius
Copy link

My two cents...

Why not have a special "marker class" to carry two facts combined: 1) the value is null and 2) type is that type.?
For example, if we have class like this:

public class NullValueOf<T> where T : class
{
	public static implicit operator T(NullValueOf<T> value) => null;
}

we can even tell C# compiler what type to choose replacing argument; foo1 and foo2 are equivalent:

var foo1 = new Foo((A)null);
var foo2 = new Foo(new NullValueOf<A>());

So ProxyGenerator can choose correct constructor either by using "hardcoded NullValueOf class", or in a more flexible way, going through user defined conversion operators.

var proxy = (Foo)generator.CreateClassProxy(typeof(Foo), new object[] { new NullValueOf<A>() }, new StandardInterceptor());

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants