在C#中,是前pression API比反射更好反射、pression、API

2023-09-02 10:53:56 作者:赱ㄋ⒐卟要迴頭

今天,我在探索C#防爆pression的API。所以,我可以利用一些帮助理解它是如何工作的,其中包括与前pression与思考的区别。我也想知道,如果前pressions只是语法糖,或者是他们确实比反射更好的的性能代价的?

Nowadays, I'm exploring C# Expression APIs. So I could use some help understanding how it works, including the difference between Expression and Reflection. I also want to understand if Expressions are merely syntactic sugar, or are they indeed better than Reflection performance-wise?

很好的例子,以及链接的好文章将AP preciated。 : - )

Good examples as well as links to good articles would be appreciated. :-)

推荐答案

关于调用一个方法:

直接调用不能被打败的速度明智的。

使用防爆pression API是全球类似于使用 Reflection.Emit的 Delegate.CreateDelegate 速度明智的(小差异可以衡量;一如既往地优化速度,没有测量和目标也没用)。 Direct call can't be beaten speed-wise.

Using Expression API is globally similar to using Reflection.Emit or Delegate.CreateDelegate speed-wise (Small differences could be measured; as always optimizing for speed without measurements and goals is useless).

他们都产生IL和框架将其编译为本地code在一些点。但你还是付的一个间接层次的费用用于调用委托和一个方法调用您的代理中。

They all generate IL and the framework will compile it to native code at some point. But you still pay the cost of one indirection level for calling the delegate and one method call inside your delegate.

这位前pression API是比较有限的,但幅度较简单的命令来使用,因为它不要求你学习的IL。

The expression API is more limited, but an order of magnitude simpler to use, as it doesn't require you to learn IL.

动态语言运行时可直接使用或C#4通过动态关键字添加一个小的开销,但附近逗留发光code,因为它缓存最多相关参数类型,获得其余的检查。

The Dynamic Language Runtime either used directly or via the dynamic keyword of C# 4 add a little overhead but stay near emitting code as it cache most checks related to parameter types, access and the rest.

在通过动态使用关键字它也得到了最巧妙的语法,因为它看起来像一个正常的方法调用。但是,如果你使用动态的,你被限制在方法调用,而库能够做更多的事情(见 IronPython的)

When used via the dynamic keyword it's also get the neatest syntax as it looks like a normal method call. But if you use dynamic you are limited to method calls while the library is able to do a lot more (See IronPython)

乔恩斯基特也得到这样的回答一些好点:Delegate.CreateDelegate VS DynamicMethod的VS防爆pression

Jon Skeet also get some good points in this answer : Delegate.CreateDelegate vs DynamicMethod vs Expression

有些样品,做同一件事情不同的方式。

Some samples, the same thing done different ways.

您已经可以从线路数量和复杂哪些解决方案易于维护,应避免从一个长期维护的立场看。

You could already see from the line count and complexity which solutions are easy to maintain and which should be avoided from a long term maintenance standpoint.

大多数样本是毫无意义的,但他们表现出基本的code代班/ C#的语法,更多信息总有MSDN 的

PS:转储是一个 LINQPad 方法

PS: Dump is a LINQPad method.

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();
    }
}