13

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

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

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

干杯

4

6 回答 6

5

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

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();
    }
}
于 2012-09-22T06:29:45.907 回答
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);

但是,如果类型是枚举或其他值类型,它将不起作用。

于 2012-09-19T23:05:17.153 回答
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*/)
                    }
                );

于 2019-02-07T14:57:28.443 回答
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[]{});
于 2021-06-09T09:18:03.523 回答
-1

使用以下代码创建一个委托,该委托在类型参数中后期绑定。另请参阅如何:使用反射检查和实例化泛型类型

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"装箱值类型。

  • 更短,更不容易出错。

于 2012-09-09T12:34:35.540 回答
-2

简短的回答是创建一个委托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; 这是因为事实证明,委托参数实际上编译为两个参数:函数的目标对象和指向函数的指针。我不能因为那里花哨的步法而受到赞扬。从这个关于如何使用反射传递委托参数的优秀答案中“借用”信息。

于 2012-08-26T16:37:17.347 回答