.net-在C#中,表达API是否比反射更好

如今,我正在探索C#Expression API。 因此,我可以借助一些帮助来了解其工作原理,包括表达与反思之间的区别。 我还想了解表达式是否仅是语法糖,还是确实比反射性能更好?

好的例子以及到好的文章的链接将不胜感激。 :-)

Nawaz asked 2020-08-11T05:41:51Z
3个解决方案
48 votes

关于调用一种方法:

  • 直接通话不能被打败。
  • 使用Expression API总体上类似于在速度上使用System.Reflection.MethodInfo.InvokeDelegate.CreateDelegate(可以测量出很小的差异;因为始终不进行测量和目标来进行速度优化是没有用的)。

    它们都会生成IL,并且框架会在某个时候将其编译为本地代码。 但是您仍然要付出一个间接级别来调用委托,并在委托内部进行一个方法调用。

    表达式API的局限性更大,但使用起来却更简单,因为它不需要您学习IL。

  • 直接使用或通过C#4的300625584372262658816关键字使用的动态语言运行时会增加一点开销,但由于它会缓存与参数类型,访问权限和其余参数有关的大多数检查,因此几乎不会产生代码。

    通过System.Reflection.MethodInfo.Invoke关键字使用时,它也获得了最简洁的语法,因为它看起来像普通的方法调用。 但是,如果您使用动态方法,则只能进行方法调用,而库可以做更多的工作(请参见IronPython)

  • System.Reflection.MethodInfo.Invoke速度很慢:除其他方法外,每次调用该方法时,还需要根据MethodInfo32检查访问权限,检查参数计数,类型,...。

乔恩·斯凯特(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();
    }
}
Julien Roncaglia answered 2020-08-11T05:42:51Z
4 votes

反射的确会变慢。 想要获得一篇不错的文章,请参阅本文。

Cornelius answered 2020-08-11T05:43:12Z
2 votes

这个家伙实际上测量了它。

[HTTP://呜呜呜.Palm Media.的/blog/2012/2/4/reflection-vs-compiled-expressions-vs-delegates-performance-com Paris ion]

简而言之:编译后的表达式被缓存到一个静态var中并被重用-比反射的执行速度快20倍。

jazzcat answered 2020-08-11T05:43:41Z
translate from https://stackoverflow.com:/questions/4803272/in-c-is-expression-api-better-than-reflection