【问题标题】:Lambda Expression Delegate Strong Type vs Weak Type implicit Convert MethodLambda 表达式委托强类型与弱类型隐式转换方法
【发布时间】:2014-04-28 21:06:17
【问题描述】:

我有一个扩展方法,它曾经采用强类型 Expression<Func<>> 参数,但出于实现原因,我不得不将其更改为使用弱类型版本。这对表达式参数产生了奇怪的影响,因为它现在似乎将 lambda 表达式包装在对“Convert”方法的显式调用中。

以前的参数看起来像:

m => m.Data

现在它看起来像下面这样:

m => Convert(m.Data)

我已使用以下示例代码复制了该问题:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;

namespace ConsoleApplication
{
    static class Program
    {
        static void Main(string[] args)
        {
            Model model = new Model()
            {
                Data = 123
            };

            Test(m => m.Data, m => m.Data);

            Console.ReadLine();                
        }

        public static void Test<TProperty>(Expression<Func<Model, TProperty>> strongTyped, Expression<Func<Model, object>> weakTyped)
        {
            Console.WriteLine("Strong Typed: {0}", strongTyped);
            Console.WriteLine("Weak Typed: {0}", weakTyped);
        }
    }

    public class Model
    {
        public int Data
        {
            get;
            set;
        }
    }
}

其输出如下:

Strong Typed: m => m.Data
Weak Typed: m => Convert(m.Data)

我猜这与将值类型自动装箱为对象类型有关。任何人都可以确认这一点或有人知道发生了什么吗?还有谁知道 Convert 方法是在哪里声明的?

在弱类型表达式上调用 compile 方法会得到以下结果:

weakTyped.Compile().Method
{System.Object lambda_method(System.Runtime.CompilerServices.Closure, ConsoleApplication.Model)}
    [System.Reflection.Emit.DynamicMethod.RTDynamicMethod]: {System.Object lambda_method(System.Runtime.CompilerServices.Closure, ConsoleApplication.Model)}
    base {System.Reflection.MethodBase}: {System.Object lambda_method(System.Runtime.CompilerServices.Closure, ConsoleApplication.Model)}
    MemberType: Method
    ReturnParameter: null
    ReturnType: {Name = "Object" FullName = "System.Object"}
    ReturnTypeCustomAttributes: {System.Reflection.Emit.DynamicMethod.RTDynamicMethod.EmptyCAHolder}

【问题讨论】:

    标签: c# lambda


    【解决方案1】:

    Convert 根本不是一种方法——它是一个UnaryExpression,而且确实正是你提出理论的原因——拳击/类型强制。有趣的是,当表达式树生成时,我们通常知道是隐式的东西实际上是显式地出现的。

    如果您自己构建表达式,则可以通过调用Expression.Convert()获得相同的效果:

    创建一个表示类型转换操作的 UnaryExpression。

    【讨论】:

    • 谢谢 - 这很有道理。我没有意识到自动装箱会应用于表达式,我认为它会在计算表达式时应用于结果。
    • @TomMaher 是的。我还能做些什么来回答这个问题?
    • 不,这很好,只是想知道发生了什么以及转换“方法”的来源。再次感谢。
    【解决方案2】:

    是的,Conversion expression 代表拳击,而不仅仅是拳击。它包含用户定义的转换等。

    例如,如果一个类型定义了将使用“转换表达式”进行转换的用户定义转换。在这种情况下,weakTyped.Body.Method 将返回类似op_Implicit... 的重载方法

    你可以用下面的代码证明这一点。

    public static void Test<TProperty>(Expression<Func<Model, TProperty>> strongTyped, Expression<Func<Model, object>> weakTyped)
    {
        var expr = (UnaryExpression)weakTyped.Body;
        Console.WriteLine("Weak Typed method: {0}", expr.Method);
        Console.WriteLine("Strong Typed: {0}", strongTyped);
        Console.WriteLine("Weak Typed: {0}", weakTyped);
    }
    public static void TestFloat<TProperty>(Expression<Func<Model, TProperty>> strongTyped, Expression<Func<Model, decimal>> weakTyped)
    {
        var expr = (UnaryExpression) weakTyped.Body;
        Console.WriteLine("Weak Typed method: {0}", expr.Method);
        Console.WriteLine("Strong Typed: {0}", strongTyped);
        Console.WriteLine("Weak Typed: {0}", weakTyped);
    }
    

    对于十进制类型,这将返回重载运算符,其中 object weakTyped.Body.Method 将为空,因为它只是一个装箱转换。

    【讨论】:

      【解决方案3】:

      转换必须包含在表达式树中,因为 object 与给定参数不同,但由于它是一个泛型方法,您可以通过包含泛型返回属性来使弱变强

      public static void Test<TProperty, TReturnValue>(Expression<Func<Model, TProperty>> strongTyped, Expression<Func<Model, TReturnValue>> weakTyped)
      {
          Console.WriteLine("Strong Typed: {0}", strongTyped);
          Console.WriteLine("Weak (now also strong) Typed: {0}", weakTyped);
      }
      

      您仍然可以使用相同的调用Test(m =&gt; m.Data, m =&gt; m.Data);,并且 TProperty 和 TReturnValue 都将由编译器解析

      【讨论】:

      • 感谢您的回复 - 不幸的是,我不得不将表达式保留为弱形式,因为我希望将一组表达式作为参数传入,所有这些表达式都可能具有不同的返回类型: Test&lt;TModel&gt;(this TModel model, params Expression&lt;Func&lt;TModel, object&gt;&gt;[] props)
      猜你喜欢
      • 2016-04-15
      • 1970-01-01
      • 1970-01-01
      • 2015-06-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-06-18
      相关资源
      最近更新 更多