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[]{});