How can I dynamically create an Action <T> at runtime?

I want at runtime to be able to execute the equivalent of the following:

var action = new Action<ANYTHING AT RUNTIME>(obj => Console.WriteLine("Called = " + obj)); 

I know that I need to get the correct type for Action, but I don’t know how to get the last bit using Delegate.Create. Type represent T in the definition of Action.

 var actionType = typeof(Action<>).MakeGenericType(Type); var constructor = actionType.GetConstructors()[0]; var @delegate = Delegate.CreateDelegate(actionType, <WHAT GOES HERE>); 

The point people seem to be missing, I'm trying to create an Action instance where T cannot be specified statically because it is used from a class derived from an attribute - this means that T can be anything, and it cannot be defined as a general definition

Greetings

+8
generics reflection c # delegates
source share
4 answers

If you know that you need to perform operations, and how to perform it regardless of type (as in your example), why not just make a general method that performs the operation and create your delegate this way?

 class Program { public static void Perform<T>(T value) { Console.WriteLine("Called = " + value); } public static Delegate CreateAction(Type type) { var methodInfo = typeof (Program).GetMethod("Perform").MakeGenericMethod(type); var actionT = typeof (Action<>).MakeGenericType(type); return Delegate.CreateDelegate(actionT, methodInfo); } static void Main(string[] args) { CreateAction(typeof (int)).DynamicInvoke(5); Console.ReadLine(); } } 
+1
source share

You can use the following code, it works if the type can be added to the object:

 Action<object> func = o => Console.WriteLine("Called = " + o.GetType().Name); var actionType = typeof(Action<>).MakeGenericType(type); var constructor = actionType.GetConstructors()[0]; var @delegate = Delegate.CreateDelegate(actionType, func.Method); 

However, if the type is an enumeration or another type of value, it will not work.

0
source share

The short answer is to create a MyActionDelegate delegate and then use:

 delegate void MyActionDelegate(T arg); Delegate @delegate = new MyActionDelegate((a) => Console.WriteLine(a)); 

Here is a working example using a generic class:

 public class MyClass<T> { public delegate void ActionDelegate(T arg); public void RunGenericAction(T arg) { var actionType = typeof(Action<>).MakeGenericType(typeof(T)); var constructor = actionType.GetConstructors()[0]; Delegate @delegate = new ActionDelegate((a) => { Console.WriteLine(arg); }); var inst = (Action<T>)constructor.Invoke(new object[] { @delegate.Target, @delegate.Method.MethodHandle.GetFunctionPointer() }); inst(arg); } } 

Use it to print 123 to the console:

 var c = new MyClass<int>(); c.RunGenericAction(123); 

You will notice that I pass two parameters to Constructor.Invoke ; that, since it turns out that the delegate argument is actually compiled as two arguments: the target of the function and a pointer to the function. I cannot take responsibility for the fancy footwork; β€œborrowed” information from this excellent answer on how to pass a delegate argument using reflection.

-one
source share

Use the following code to create a delegate that is associated with a delay in the type parameter. See Also Practical Guide. Studying and creating generic types with reflection .

 abstract class ActionHelper { protected abstract Delegate CreateActionImpl(); // A subclass with a static type parameter private class ActionHelper<T> : ActionHelper { protected override Delegate CreateActionImpl() { // create an Action<T> and downcast return new Action<T>(obj => Console.WriteLine("Called = " + (object)obj)); } } public static Delegate CreateAction(Type type) { // create the type-specific type of the helper var helperType = typeof(ActionHelper<>).MakeGenericType(type); // create an instance of the helper // and upcast to base class var helper = (ActionHelper)Activator.CreateInstance(helperType); // call base method return helper.CreateActionImpl(); } } // Usage // Note: The "var" is always "Delegate" var @delegate = ActionHelper.CreateAction(anyTypeAtRuntime); 

However, I do not recommend using this method. Use instead

 Action<object> action = obj => Console.WriteLine("Called = " + obj); 

He offers

  • The same functionality.

    Your source code "Called = " + obj" calls .ToString() in the parameter. Same as above.

  • There is no difference in performance.

    If obj is a value type, both options perform a boxing operation. Boxing in the first is not obvious, but "Called = " + obj" shows type values.

  • Shorter and less error prone.

-one
source share

All Articles