.net-在C#中,表达API是否比反射更好
如今,我正在探索C#Expression API。 因此,我可以借助一些帮助来了解其工作原理,包括表达与反思之间的区别。 我还想了解表达式是否仅是语法糖,还是确实比反射性能更好?
好的例子以及到好的文章的链接将不胜感激。 :-)
关于调用一种方法:
- 直接通话不能被打败。
使用Expression API总体上类似于在速度上使用
System.Reflection.MethodInfo.Invoke
或Delegate.CreateDelegate
(可以测量出很小的差异;因为始终不进行测量和目标来进行速度优化是没有用的)。它们都会生成IL,并且框架会在某个时候将其编译为本地代码。 但是您仍然要付出一个间接级别来调用委托,并在委托内部进行一个方法调用。
表达式API的局限性更大,但使用起来却更简单,因为它不需要您学习IL。
直接使用或通过C#4的300625584372262658816关键字使用的动态语言运行时会增加一点开销,但由于它会缓存与参数类型,访问权限和其余参数有关的大多数检查,因此几乎不会产生代码。
通过
System.Reflection.MethodInfo.Invoke
关键字使用时,它也获得了最简洁的语法,因为它看起来像普通的方法调用。 但是,如果您使用动态方法,则只能进行方法调用,而库可以做更多的工作(请参见IronPython)System.Reflection.MethodInfo.Invoke
速度很慢:除其他方法外,每次调用该方法时,还需要根据MethodInfo
32检查访问权限,检查参数计数,类型,...。
乔恩·斯凯特(Jon Skeet)在此答案中也得到一些好处:Delegate.CreateDelegate与DynamicMethod与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();
}
}
反射的确会变慢。 想要获得一篇不错的文章,请参阅本文。
这个家伙实际上测量了它。
[HTTP://呜呜呜.Palm Media.的/blog/2012/2/4/reflection-vs-compiled-expressions-vs-delegates-performance-com Paris ion]
简而言之:编译后的表达式被缓存到一个静态var中并被重用-比反射的执行速度快20倍。