C# 如何动态创建动作<;T>;在运行时?

C# 如何动态创建动作<;T>;在运行时?,c#,.net,generics,reflection,delegates,C#,.net,Generics,Reflection,Delegates,我希望能够在运行时执行以下等效操作: var action = new Action<ANYTHING AT RUNTIME>(obj => Console.WriteLine("Called = " + obj)); 人们似乎缺少的一点是,我试图创建一个动作实例,其中T不能静态指定,因为它是从属性派生的类中使用的-这意味着T可以是任何东西,也不能定义为泛型定义 干杯简短的回答是创建一个代理MyActionDelegate,然后使用: delegate void MyActi

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

var action = new Action<ANYTHING AT RUNTIME>(obj => Console.WriteLine("Called = " + obj));
人们似乎缺少的一点是,我试图创建一个动作实例,其中T不能静态指定,因为它是从属性派生的类中使用的-这意味着T可以是任何东西,也不能定义为泛型定义


干杯

简短的回答是创建一个代理
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);
    }
}

您会注意到我将两个参数传递给
构造函数;这是因为事实证明委托参数实际上编译为两个参数:函数的目标对象和指向函数的指针。我不能因为在那里的华丽步法而受到赞扬;使用以下代码创建委托,该委托在类型参数中后期绑定。另见

抽象类ActionHelper{
受保护的抽象委托CreateActionImpl();
//具有静态类型参数的子类
私有类ActionHelper:ActionHelper{
受保护的重写委托CreateActionImpl(){
//创建一个动作并向下播放
返回新操作(obj=>Console.WriteLine(“Called=”+(object)obj));
}
}
公共静态委托CreateAction(类型){
//创建特定于类型的辅助对象类型
var helperType=typeof(ActionHelper).MakeGenericType(type);
//创建辅助对象的实例
//并向上转换为基类
var helper=(ActionHelper)Activator.CreateInstance(helperType);
//调用基方法
返回helper.CreateActionImpl();
}
}
//用法
//注意:“var”始终是“Delegate”
var@delegate=ActionHelper.CreateAction(anyTypeAtRuntime);
也就是说,我不建议使用这种方法。相反,使用

Action<object> action = obj => Console.WriteLine("Called = " + obj);
Action-Action=obj=>Console.WriteLine(“Called=“+obj”);
它提供

  • 同样的功能

    您的原始代码调用了参数上的
    “Called=“+obj”
    调用了
    .ToString()
    。上述操作也是如此

  • 没有性能差异

    如果
    obj
    参数是值类型,则两个变量都会执行装箱操作。第一个变量中的装箱不明显,但
    “Called=“+obj”
    会装箱值类型

  • 更短,更不容易出错


您可以使用以下代码,如果可以将类型强制转换为对象,则该代码有效:

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);
Action func=o=>Console.WriteLine(“Called=“+o.GetType().Name”);
var actionType=typeof(操作)。MakeGenericType(类型);
var constructor=actionType.GetConstructors()[0];
var@delegate=delegate.CreateDelegate(actionType,func.Method);

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

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

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();
    }
}
类程序
{
公共静态无效执行(T值)
{
Console.WriteLine(“调用=”+值);
}
公共静态委托CreateAction(类型)
{
var methodInfo=typeof(Program).GetMethod(“Perform”).MakeGenericMethod(type);
var actionT=typeof(Action).MakeGenericType(type);
返回Delegate.CreateDelegate(actionT,methodInfo);
}
静态void Main(字符串[]参数)
{
CreateAction(typeof(int)).DynamicInvoke(5);
Console.ReadLine();
}
}
多亏了“@prashanth”的建议,我成功地创建并调用了一个具有运行时类型的操作,多亏了dynamic关键字:

公共操作GetDynamicAction(/*某些参数*/)
{
返回oDyn=>
{
//下面是使用/*一些参数的操作代码*/
};
}
具有基本操作处理程序的示例:

公共类ActionHandler
{
公共返回类型DoAction(Action t)
{
//需要什么
}
}
用例:

/*some params*/=任何特定于运行时类型的数据(在我的示例中,我将一个类型和一个MethodInfo作为参数传递,并在操作中调用)
var genericMethod=actionHandler.GetType().GetMethod(nameof(actionHandler.DoAction));
var method=genericMethod.MakeGenericMethod(runtimeGenericType);
var actionResult=(ReturnType)method.Invoke(actionHandler,新对象[]
{
GetDynamicAction(/*某些参数*/)
}
);

创建一个泛型方法,以生成具有所需泛型参数的操作:

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

action=newaction(obj=>Console.WriteLine(“Called=“+obj”)的哪一部分是否要动态生成?我想创建\初始化参数'action',我很困惑。没有名为
action
的参数。你想实现什么?简单地说,当T只在运行时知道,你不能静态地推断它时,你如何在运行时创建一个操作实例?如果你在.NET 4上,你可以尝试操作“无性能差异”到什么?您还没有提供任何实际动态创建委托的代码。您可以方便地为抽象类提供一个抽象方法
CreateActionImpl
,但不提供实现——这是唯一可以动态创建操作的方法。@Peter我已经在声明后面的段落中解释了性能影响。此外,OP只需要委托延迟绑定的类型参数。代码在没有Reflection.Emit的情况下执行该操作。
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);
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();
    }
}
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[]{});