如何在运行时动态创建Action <t>?</t>

时间:2012-08-26 15:22:11

标签: c# .net generics reflection delegates

我希望能够在运行时执行与以下操作相同的操作:

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

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

var actionType = typeof(Action<>).MakeGenericType(Type);
var constructor = actionType.GetConstructors()[0];
var @delegate = Delegate.CreateDelegate(actionType, <WHAT GOES HERE>);
人们似乎缺少的一点是我正在尝试创建一个Action实例,其中T不能静态指定,因为它是从一个派生自Attribute的类中使用的 - 这意味着T可以是任何东西而且它不能被定义为通用定义

干杯

6 个答案:

答案 0 :(得分:1)

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

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 :(得分:0)

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

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

但是,如果type为enum或其他值类型,则无效。

答案 2 :(得分:0)

多亏了“ @prashanth”的建议,借助dynamic关键字,我设法以运行时类型动态创建并调用Action <>:

        public Action<dynamic> GetDynamicAction(/* some params */)
        {
            return oDyn =>
            {
                //here is the action code with the use of /* some params */
            };
        }

带有基本动作处理程序的示例:

public class ActionHandler
{
     public ReturnType DoAction<T>(Action<T> t)
     {
         //whatever needed
     }
}

用例:

/* some params */ = Any runtime type specific data (in my case I had a Type and a MethodInfo passed as parameters and that were called in the action)

var genericMethod = actionHandler.GetType().GetMethod(nameof(ActionHandler.DoAction));
var method = genericMethod.MakeGenericMethod(runtimeGenericType);

var actionResult = (ReturnType) method.Invoke(actionHandler, new object[]
                    {
                        GetDynamicAction(/*some params*/)
                    }
                );

答案 3 :(得分:0)

创建一个泛型方法来生成一个带有所需泛型参数的 Action:

private Action<T> CreateAction<T>() => 
             new Action<T>(obj => Console.WriteLine("Called = " + (object)obj)); 

这样称呼它:

var action = GetType()
            .GetMethod(nameof(CreateAction), BindingFlags.NonPublic | BindingFlags.Instance)
            ?.MakeGenericMethod(type)
            ?.Invoke(this, new object[]{});

答案 4 :(得分:-1)

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

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

以下是使用泛型类的工作示例:

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

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

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

你会注意到我将两个参数传递给Constructor.Invoke;这是因为事实证明,委托参数实际上编译为两个参数:函数的目标对象和指向函数的指针。我不能因为那里花哨的步法而受到赞扬;来自this excellent answer on how to pass a delegate argument using reflection.

的“借来的”信息

答案 5 :(得分:-1)

使用以下代码创建委托,该委托在type参数中后期绑定。另请参阅How to: Examine and Instantiate 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);

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

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

提供

  • 相同的功能。

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

  • 没有性能差异。

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

  • 更短,更不容易出错。