在C#中,Expression API比Reflection更好 [英] In C#, Is Expression API better than Reflection

查看:104
本文介绍了在C#中,Expression API比Reflection更好的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

现在我正在探索C#Expression API。所以我可以用一些帮助来理解它的工作原理,包括Expression和Reflection之间的区别。我还想了解表达式是否只是句法糖,或者它们的确比反思表现更好吗?



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

解决方案

关于调用一种方法:




  • 直接呼叫不能以速度方式殴打。

  • 使用Expression API与全局相似,使用 Reflection.Emit delegate.CreateDelegate 速度(可以测量微小的差异;一如以往的优化速度,没有测量和目标是无用的)。



    他们都生成IL,框架将在某些时候将其编译为本地代码。但是您仍然支付一个间接级别的成本,用于调用代理和一个方法调用。代理中的一个方法调用。

    表达式API更有限,但更简单一个数量级使用,因为它不需要你学习IL。


  • 动态语言运行时可以直接使用或通过 / code>关键字C#4添加一点开销,但保持接近发出代码,因为它缓存与参数类型,访问和其余的大多数检查。



    当通过动态关键字使用,它也得到最整洁的语法,因为它看起来像一个普通的方法调用。但是,如果您使用动态,则只能使用方法调用,而库可以执行更多操作(请参阅 IronPython


  • System.Reflection.MethodInfo.Invoke 慢:除了需要检查访问的其他方法之外每次调用该方法时,都可以使用 MethodInfo 来检查参数count,type,...。$ /


Jon Skeet在此答案中也得到了一些好处:代表。 CreateDelegate vs DynamicMethod vs Expression






某些示例,同样的事情做了不同的方式。



您可以从行数和复杂性中看出,哪些解决方案易于维护,哪些应该从长期维护的角度来避免。



大多数样品是无意义的,但是t嘿演示C#的基本代码生成类/语法,更多信息总是有MSDN



PS:转储是一个 LINQPad 方法。

  public class Foo 
{
public string Bar(int value){return value.ToString(); }
}

void Main()
{
object foo = new Foo();

//我们有一个东西的实例,想要调用一个这样的签名的方法:
// public string Bar(int value);

Console.WriteLine(Cast and Direct method call);
{
var result =((Foo)foo).Bar(42);
result.Dump();
}
Console.WriteLine(在本地作用域上创建一个lambda关闭);
{
//无用,但我会在最后手动执行一次

Func< int,string> func = i => ((富)富)标尺示(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(使用动态关键字);
{
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(创建一个表达式并编译它以在一个实例上调用委托);
{
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 var func = lambda.Compile();
var result = func(42);
result.Dump();
}
Console.WriteLine(创建一个表达式并将其编译为可以在任何实例上调用的委托);
{
//请注意,在这种情况下,Foo在编译时必须知道,显然在这种情况下,您需要
//才能调用方法,否则只需调用它!
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 var func = lambda.Compile();
var result = func((Foo)foo,42);
result.Dump();
}
Console.WriteLine(创建一个DynamicMethod并将其编译为可以在任何实例上调用的委托);
{
//与上一个表达式示例相同。在编译时需要知道Foo,需要向代理提供
//。

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(模拟没有关闭和更多行的关闭...);
{
var type = foo.GetType();
var method = type.GetMethod(Bar);

// Foo类必须是public才能使其工作,
// DynamicMethod.CreateDelegate的skipVisibility参数无法在不破坏.Net安全模型的情况下进行仿真。

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


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?

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

解决方案

Regarding calling one method :

  • 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).

    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.

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

  • 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.

    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)

  • System.Reflection.MethodInfo.Invoke is slow : in addition to what other methods do it need to check access rights, check arguments count, type, ... against the MethodInfo each time you call the method.

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.

Most of the samples are pointless but they demonstrate the basic code generation classes / syntaxes of C#, for more info there is always the MSDN

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

这篇关于在C#中,Expression API比Reflection更好的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆