Относительно вызова одного метода:
- Прямой вызов нельзя превзойти по скорости.
Использование Expression API в целом аналогично использованию Reflection.Emit
или Delegate.CreateDelegate
по скорости (можно измерить небольшие различия; так как всегда оптимизировать скорость без измерений и целей бесполезно).
Все они генерируют IL, и в какой-то момент среда скомпилирует его в собственный код.Но вы по-прежнему платите стоимость одного уровня косвенности для вызова делегата и одного вызова метода внутри вашего делегата.
API выражений более ограничен, но его использование на порядок проще, так как для этого не требуетсявы должны изучить IL.
Динамическая языковая среда выполнения, используемая непосредственно или через ключевое слово dynamic
в C # 4, добавляет немного накладных расходов, но остается вблизи выдачи кода, поскольку она кэширует большинство проверок, связанных стипы параметров, доступ и остальное.
При использовании через ключевое слово dynamic
он также получает самый синтаксический синтаксис, похожий на обычный вызов метода.Но если вы используете динамический, вы ограничены вызовами методов, в то время как библиотека может делать намного больше (см. IronPython )
System.Reflection.MethodInfo.Invoke
медленно: в дополнение к тому, чтодругие методы должны проверять права доступа, проверять количество аргументов, вводить ... против MethodInfo
при каждом вызове метода.
Джон Скит также получил несколько хороших баллов в этом ответе: Delegate.CreateDelegate vs DynamicMethod vs Expression
Некоторые примеры, то же самое сделано разными способами.
Вы уже могли видеть по количеству строк и сложностиРешения просты в обслуживании и их следует избегать с точки зрения долгосрочного обслуживания.
Большинство примеров бессмысленны, но они демонстрируют основные классы / синтаксисы генерации кода C #, для получения дополнительной информации естьвсегда MSDN
PS: дамп - это метод LINQPad .
public class Foo
{
public string Bar(int value) { return value.ToString(); }
}
void Main()
{
object foo = new Foo();
// We have an instance of something and want to call a method with this signature on it :
// public string Bar(int value);
Console.WriteLine("Cast and Direct method call");
{
var result = ((Foo)foo).Bar(42);
result.Dump();
}
Console.WriteLine("Create a lambda closing on the local scope.");
{
// Useless but i'll do it at the end by manual il generation
Func<int, string> func = i => ((Foo)foo).Bar(i);
var result = func(42);
result.Dump();
}
Console.WriteLine("Using MethodInfo.Invoke");
{
var method = foo.GetType().GetMethod("Bar");
var result = (string)method.Invoke(foo, new object[] { 42 });
result.Dump();
}
Console.WriteLine("Using the dynamic keyword");
{
var dynamicFoo = (dynamic)foo;
var result = (string)dynamicFoo.Bar(42);
result.Dump();
}
Console.WriteLine("Using CreateDelegate");
{
var method = foo.GetType().GetMethod("Bar");
var func = (Func<int, string>)Delegate.CreateDelegate(typeof(Func<int, string>), foo, method);
var result = func(42);
result.Dump();
}
Console.WriteLine("Create an expression and compile it to call the delegate on one instance.");
{
var method = foo.GetType().GetMethod("Bar");
var thisParam = Expression.Constant(foo);
var valueParam = Expression.Parameter(typeof(int), "value");
var call = Expression.Call(thisParam, method, valueParam);
var lambda = Expression.Lambda<Func<int, string>>(call, valueParam);
var func = lambda.Compile();
var result = func(42);
result.Dump();
}
Console.WriteLine("Create an expression and compile it to a delegate that could be called on any instance.");
{
// Note that in this case "Foo" must be known at compile time, obviously in this case you want
// to do more than call a method, otherwise just call it !
var type = foo.GetType();
var method = type.GetMethod("Bar");
var thisParam = Expression.Parameter(type, "this");
var valueParam = Expression.Parameter(typeof(int), "value");
var call = Expression.Call(thisParam, method, valueParam);
var lambda = Expression.Lambda<Func<Foo, int, string>>(call, thisParam, valueParam);
var func = lambda.Compile();
var result = func((Foo)foo, 42);
result.Dump();
}
Console.WriteLine("Create a DynamicMethod and compile it to a delegate that could be called on any instance.");
{
// Same thing as the previous expression sample. Foo need to be known at compile time and need
// to be provided to the delegate.
var type = foo.GetType();
var method = type.GetMethod("Bar");
var dynamicMethod = new DynamicMethod("Bar_", typeof(string), new [] { typeof(Foo), typeof(int) }, true);
var il = dynamicMethod.GetILGenerator();
il.DeclareLocal(typeof(string));
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Call, method);
il.Emit(OpCodes.Ret);
var func = (Func<Foo, int, string>)dynamicMethod.CreateDelegate(typeof(Func<Foo, int, string>));
var result = func((Foo)foo, 42);
result.Dump();
}
Console.WriteLine("Simulate closure without closures and in a lot more lines...");
{
var type = foo.GetType();
var method = type.GetMethod("Bar");
// The Foo class must be public for this to work, the "skipVisibility" argument of
// DynamicMethod.CreateDelegate can't be emulated without breaking the .Net security model.
var assembly = AppDomain.CurrentDomain.DefineDynamicAssembly(
new AssemblyName("MyAssembly"), AssemblyBuilderAccess.Run);
var module = assembly.DefineDynamicModule("MyModule");
var tb = module.DefineType("MyType", TypeAttributes.Class | TypeAttributes.Public);
var fooField = tb.DefineField("FooInstance", type, FieldAttributes.Public);
var barMethod = tb.DefineMethod("Bar_", MethodAttributes.Public, typeof(string), new [] { typeof(int) });
var il = barMethod.GetILGenerator();
il.DeclareLocal(typeof(string));
il.Emit(OpCodes.Ldarg_0); // this
il.Emit(OpCodes.Ldfld, fooField);
il.Emit(OpCodes.Ldarg_1); // arg
il.Emit(OpCodes.Call, method);
il.Emit(OpCodes.Ret);
var closureType = tb.CreateType();
var instance = closureType.GetConstructors().Single().Invoke(new object[0]);
closureType.GetField(fooField.Name).SetValue(instance, foo);
var methodOnClosureType = closureType.GetMethod("Bar_");
var func = (Func<int, string>)Delegate.CreateDelegate(typeof(Func<int, string>), instance,
closureType.GetMethod("Bar_"));
var result = func(42);
result.Dump();
}
}