8

Using reflection, I'm trying to create a delegate from a parameterless constructor like this:

Delegate del = GetMethodInfo( () => System.Activator.CreateInstance( type ) ).CreateDelegate( delType );

static MethodInfo GetMethodInfo( Expression<Func<object>> func )
{
    return ((MethodCallExpression)func.Body).Method;
}

But I get this exception: "Cannot bind to the target method because its signature or security transparency is not compatible with that of the delegate type." What will work?

Note that CreateDelegate was moved, for this profile at least, since the previous version of .NET. Now it's on MethodInfo.

HappyNomad
  • 4,458
  • 4
  • 36
  • 55
  • Are you trying to create a delegate that invokes a parameterless constructor of a type where you only have a Type instance? – Peter Ritchie May 15 '12 at 03:20

3 Answers3

13

As phoog points out a constructor doesn't "return" a value; plus you get information about it with ConstructorInfo and not MethodInfo; which means you can't create a delegate around it directly. You have to create code that invokes the constructor and returns the value. For example:

var ctor = type.GetConstructor(Type.EmptyTypes);
if (ctor == null) throw new MissingMethodException("There is no constructor without defined parameters for this object");
DynamicMethod dynamic = new DynamicMethod(string.Empty,
            type,
            Type.EmptyTypes,
            type);
ILGenerator il = dynamic.GetILGenerator();

il.DeclareLocal(type);
il.Emit(OpCodes.Newobj, ctor);
il.Emit(OpCodes.Stloc_0);
il.Emit(OpCodes.Ldloc_0);
il.Emit(OpCodes.Ret);

var func = (Func<object>)dynamic.CreateDelegate(typeof(Func<object>));

Of course, if you don't know the type at compile time then you can only deal with Object...

Peter Ritchie
  • 35,463
  • 9
  • 80
  • 98
  • My code is working this way. Thanks! Can you also explain why your approach works but mine doesn't? I was also trying to "create a delegate around" the constructor. – HappyNomad May 15 '12 at 04:16
  • What is the meaning of `stloc.0` and `ldloc.0`? Imagine that you're holding in your hands something that you need, but to take it, you put it in a box, so that now you can get it out of there. – Kir_Antipov Mar 28 '20 at 16:21
  • stdloc.0 means `Pops the current value from the top of the evaluation stack and stores it in the local variable list at index 0.` and ldloc.0 means `Loads the local variable at index 0 onto the evaluation stack.` The evaluation stack is like a register if you're familiar with machine language. – Peter Ritchie Apr 05 '20 at 21:20
  • `DeclareLocal`, `Stloc_0` and `LdLoc_0` can be removed – TheConstructor Oct 23 '20 at 13:10
10

Try it out:

Dictionary<Type, Delegate> cache = new Dictionary<Type, Delegate>();
public T Create<T>()
{
    if (!cache.TryGetValue(typeof(T), out var d))
        d = cache[typeof(T)]
            = Expression.Lambda<Func<T>>(
                Expression.New(typeof(T)),
                Array.Empty<ParameterExpression>())
            .Compile();
    return ((Func<T>)d)();
}

The reflection is too slow! Speed tests are there (in Russian): https://ru.stackoverflow.com/a/860921/218063

Андрей NOP
  • 203
  • 5
  • 16
  • 2
    Thanks! This helped me out. If you want parameters too, make sure the New call args are passed to the Lambda call. Given a ConstructorInfo, here's my code for a TResult constructor that takes a single T parameter: 'NewExpression newExpression = Expression.New(constructorInfo, Expression.Parameter(typeof(T))); Func constructorFunc = Expression.Lambda>(newExpression, newExpression.Arguments.Cast().ToArray()).Compile();' – Bill Menees May 11 '20 at 16:42
3

It would not be very useful to have a delegate that points to a constructor, since constructors do not have a return value. The delegate would construct an object but give you no way of retaining a reference to it.

You can of course create delegates that return the newly-constructed object:

Func<object> theDelegate = () => new object();

You could also create a delegate from the Invoke() method of the constructor's ConstructorInfo

For other types of objects:

Func<string> theDelegate = () => new string('w', 3);
Func<SomeClassInMyProject> theDelegate = () => new SomeClassInMyProject();

The last line assumes there's an accessible parameterless constructor.

Update with CreateDelegate()

T CallConstructor<T>() where T : new() { return new T(); }
Delegate MakeTheDelegate(Type t)
{
    MethodInfo generic = //use your favorite technique to get the MethodInfo for the CallConstructor method
    MethodInfo constructed = generic.MakeGenericMethod(t);
    Type delType = typeof(Func<>).MakeGenericType(t);
    return constructed.CreateDelegate(delType);
}
phoog
  • 42,068
  • 6
  • 79
  • 117
  • I need a delegate, whose type exactly matches delType, that returns the created object. This code doesn't help since the result is of type Func and not delType. – HappyNomad May 15 '12 at 03:31
  • @HappyNomad the code does return the exact type of the created object, since the object is created with the statement `new object()`. I've added some examples of how to do this with other types. – phoog May 15 '12 at 03:37
  • @HappyNomad in response to your comment: if you don't know the type of the object at runtime, why do you need a strongly-typed delegate? – phoog May 15 '12 at 03:42
  • @HappyNomad And how do you propose to cast it to, for example, `Func` if you don't know `T` at compile time? – phoog May 15 '12 at 03:51
  • A useful answer needs CreateDelegate( delType ) in it. My original approach has that. Peter's answer has that. Yours doesn't. – HappyNomad May 15 '12 at 03:56
  • No since I get the same exception that's mentioned in my question. – HappyNomad May 15 '12 at 04:23
  • @HappyNomad it worked for me. I suppose there is something going on with the security transparency in your project. – phoog May 15 '12 at 04:24