如何在运行时动态创建Action ?

我希望能够在运行时执行以下等效操作:

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

我知道我需要为Action获取正确的类型,但不知道如何使用Delegate.Create获取最后一位。 在Action定义中Type表示T.

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

人们似乎缺少的一点是我正在尝试创建一个Action实例,其中T不能静态指定,因为它是从一个派生自Attribute的类中使用的 – 这意味着T可以是任何东西而且它不能被定义为通用定义

干杯

如果您知道您需要执行的操作是什么以及如何执行它而不管类型(如您的示例中)为什么不只是创建一个执行操作的通用方法并以这种方式创建您的委托?

 class Program { public static void Perform(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(); } } 

您可以使用以下代码,如果类型可以转换为对象,它可以工作:

 Action 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); 

但是,如果type是enum或其他值类型,则它将不起作用。

简短的回答是创建一个委托MyActionDelegate ,然后使用:

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

这是一个使用generics类的工作示例:

 public class MyClass { 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)constructor.Invoke(new object[] { @delegate.Target, @delegate.Method.MethodHandle.GetFunctionPointer() }); inst(arg); } } 

像这样使用它,它输出123到控制台:

 var c = new MyClass(); c.RunGenericAction(123); 

您会注意到我将两个参数传递给Constructor.Invoke ; 这是因为事实certificate,委托参数实际上编译为两个参数:函数的目标对象和指向函数的指针。 我不能因为那里花哨的步法而受到赞扬; 从这个关于如何使用reflection传递委托参数的优秀答案中 “借用”信息。

使用以下代码创建委托,该委托在type参数中后期绑定。 另请参见如何:使用reflection检查和实例化通用类型 。

 abstract class ActionHelper { protected abstract Delegate CreateActionImpl(); // A subclass with a static type parameter private class ActionHelper : ActionHelper { protected override Delegate CreateActionImpl() { // create an Action and downcast return new Action(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); 

也就是说,我不建议使用这种方法。 相反,使用

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

它提供

  • 相同的function。

    您的原始代码"Called = " + obj"在参数上调用.ToString() 。上面也是如此。

  • 没有性能差异。

    如果obj参数是值类型,则两个变体都执行装箱操作。 拳击在第一个不明显,但"Called = " + obj"框值类型。

  • 更短,更不容易出错。