可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I want to be able to do the equivalent to the following at runtime:
var action = new Action<ANYTHING AT RUNTIME>(obj => Console.WriteLine("Called = " + obj));
I know I need to get the correct type for the Action, but not sure how to get the final bit using Delegate.Create. Type
represent T in the Action definition.
var actionType = typeof(Action<>).MakeGenericType(Type);
var constructor = actionType.GetConstructors()[0];
var @delegate = Delegate.CreateDelegate(actionType, <WHAT GOES HERE>);
the point people seem to be missing is I'm trying to create an instance of Action where T can not be specified statically because it is being used from a class derived from Attribute - this means T could be anything and it can not be defines as a generic definition
Cheers
回答1:
If you know what the operation you need to perform is and how to perform it regardless of type (as in your example) why not just make a generic method that performs the operation and create your delegate that way?
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();
}
}
回答2:
You can use following code, it works if type can be casted to an object:
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);
However if type is enum or other value type it won't work.
回答3:
Thanks to "@prashanth" suggestion, I managed to dynamically create and call an Action<> with a runtime type thanks to the dynamic keyword :
public Action<dynamic> GetDynamicAction(/* some params */)
{
return oDyn =>
{
//here is the action code with the use of /* some params */
};
}
Example with a basic action handler :
public class ActionHandler
{
public ReturnType DoAction<T>(Action<T> t)
{
//whatever needed
}
}
Use case :
/* 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*/)
}
);
回答4:
The short answer is to create a delegate MyActionDelegate
and then use:
delegate void MyActionDelegate(T arg);
Delegate @delegate = new MyActionDelegate((a) => Console.WriteLine(a));
Here's a working example using a generic class:
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);
}
}
Use it like this, which outputs 123
to the console:
var c = new MyClass<int>();
c.RunGenericAction(123);
You'll notice I'm passing two parameters to Constructor.Invoke
; that's because it turns out that a delegate argument actually compiles as two arguments: the target object of the function, and a pointer to the function. I can't take credit for the fancy footwork there; "borrowed" info from this excellent answer on how to pass a delegate argument using reflection.
回答5:
Use the following code to create a delegate, which is late bound in the type parameter. See also 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);
That said, I don't recommend using this method. Instead, use
Action<object> action = obj => Console.WriteLine("Called = " + obj);
It offers
The same functionality.
Your original code "Called = " + obj"
calls .ToString()
on the parameter. So does the above.
No performance difference.
If the obj
parameter is a value type, both variants perform a boxing operation. The boxing in the first is not obvious, but "Called = " + obj"
boxes value types.
Shorter and less error-prone.