Необычно, если вам это действительно нужно и вы не возражаете бросить слишком много инфраструктуры на проблему, вы можете использовать ldvirtftn
и calli
.
Это кажется мне очень странным, поскольку я думал, что именно так и поступил делегат за кулисами:
public class MyAction{
public virtual void Invoke(SomeClass @this)
{
ldarg.1
dup
ldvirtftn SomeClass.GenericMethod<Int32>
calli void *(argument)
ret
}
Ldvirtftn
просматривает указатель на функцию, которая будет вызываться для этого конкретного метода. Если вы используете не виртуальный универсальный метод, производительность будет примерно такой же, как у делегата, привязанного к той же функции. И если это виртуальный универсальный метод, он примерно вдвое медленнее, значит, он все еще работает, так что это значительное улучшение.
Я создал это с помощью mirror.emit, и он, кажется, работает нормально, и он может вызывать закрытый виртуальный универсальный метод. К сожалению, в отличие от делегата этот тип связан с конкретным методом. Однако довольно неприятно то, что среда выполнения не позволяет создавать динамический метод, который использует код операции ldvirtftn
, ldftn
или calli
.
public class SomeType
{
public virtual void DoNothing<T>()
{
Console.WriteLine(typeof(T));
}
}
public abstract class MyAction
{
public abstract void Invoke(SomeType type);
}
public static void Main(string[] args)
{
TypeBuilder builder = AppDomain.CurrentDomain
.DefineDynamicAssembly(new AssemblyName(MethodBase.GetCurrentMethod().DeclaringType.Name),
AssemblyBuilderAccess.RunAndCollect)
.DefineDynamicModule("Module").DefineType("MyType",
TypeAttributes.AnsiClass | TypeAttributes.AutoClass | TypeAttributes.Class |
TypeAttributes.Public | TypeAttributes.Sealed,
typeof (MyAction));
var ilgen = builder.DefineMethod("Invoke",
MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Final |
MethodAttributes.Virtual,
CallingConventions.HasThis,
typeof (void), new[] {typeof (SomeType)}).GetILGenerator();
ilgen.Emit(OpCodes.Ldarg_1);
ilgen.Emit(OpCodes.Dup);
ilgen.Emit(OpCodes.Ldvirtftn, typeof (SomeType).GetMethod("DoNothing").MakeGenericMethod(typeof (int)));
ilgen.Emit(OpCodes.Calli, SignatureHelper.GetMethodSigHelper(CallingConventions.HasThis, typeof (void)));
ilgen.Emit(OpCodes.Ret);
MyAction action = Activator.CreateInstance(builder.CreateType()) as MyAction;
action.Invoke(new SomeType());
}
Если вы в порядке с генерацией кода, вы можете использовать деревья выражений или динамический метод, чтобы просто вызвать метод. Это немного медленнее, чем у прямого делегата, но мы говорим о крошечных накладных расходах.