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
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(); } } 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.
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.
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
objis 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.