【问题标题】:Implicit Cast not happening in Expression Tree表达式树中未发生隐式转换
【发布时间】:2011-05-31 12:35:55
【问题描述】:

我遇到了一个场景,我需要根据输入对不同属性的自定义类型列表进行排序。在几篇文章的帮助下,我能够提出使用 LINQ 的通用实现。在单元测试期间,其中一个测试失败了,因为在使用表达式树创建 lamda 表达式时发生了隐式转换。

下面我放了示例代码来理解这个问题(不知道为什么格式不正确,很抱歉)

static class ExtensionMethods
{
 public static IEnumerable<TSource> Sort<TSource>(this IEnumerable<TSource> unSortedList, Func<TSource, object> selector, bool isAscending)
    {
       return isAscending ? unSortedList.OrderBy(selector) :                 unSortedList.OrderByDescending(selector);
}   
}

class Program
{

    class Student
    {
        public string Name { get; set; }
        public int Age { get; set; }
    }

    static void Main(string[] args)
    {
        var unOrderedStudents = new List<Student>
                           {
                               new Student{ Name="A", Age=20},
                               new Student{Name = "B", Age=19}
                           };


        //This Works
        var sortUsingLamda = unOrderedStudents.Sort<Student>(stud => stud.Age, true);


        //Exception - Expression of type 'System.Int32' cannot be used for return type 'System.Object'
        var sortUsingExpressionTree = unOrderedStudents.Sort<Student>( GetSortFunc<Student>("Age"), true);

        Console.WriteLine("Press any key to continue");
        Console.ReadLine();
    }



    private static Func<T, object> GetSortFunc<T>(string sortColumn)
    {
        var param = Expression.Parameter(typeof(T), "entity");

        var propertyExpression = Expression.Property(param, sortColumn);

        var boxingExpression = Expression.Convert(propertyExpression, typeof(object));

        return Expression.Lambda<Func<T, object>>(propertyExpression, param).Compile();

        //after adding Convert expression issue got fixed
        //return Expression.Lambda<Func<T, object>>(boxingExpression, param).Compile();

    }
} 

在 Main 方法中,当我尝试将 Func 委托直接传递给 Sort 扩展方法时,它可以工作,但由于表达式树而失败。

我发现了一个similar 问题,但它谈到了受约束的类型参数。两个问题一样吗?有人可以帮我理解这个问题。

【问题讨论】:

    标签: c# .net linq expression-trees type-parameter


    【解决方案1】:

    您需要使用盒装版本(您目前创建 boxingExpression,但您的最终查询不是基于propertyExpression):

    return Expression.Lambda<Func<T, object>>(boxingExpression, param).Compile();
    

    关于为什么这不是隐式的 - 这里只是 没有隐式转换; Expression != C#。装箱是一项重要的操作,Expression API 需要树中的特定节点。

    【讨论】:

    • 这是有道理的。如果我没记错的话,隐式转换是在编译时完成的,而运行时完全不知道这一点。由于表达式树是在运行时执行的,因此需要一个转换节点来转换它。如果我没有正确理解,请纠正我。
    • @Moorthy - 是的,装箱在编译器中被使用(有一个操作码)。但是,将 reference-preserving 强制转换为基本类型是合法的,并且是无操作的(什么都不做) - 你可能会发现它工作正常。
    【解决方案2】:

    您将 GetSortFunc 原型设计为返回一个返回对象的 Func 实例。因此,确保您生成的表达式树产生一个对象是您的工作。

    虽然 int 在 C# 中隐式转换为 object,但它被装箱了。这就是为什么您需要在代码中使用装箱表达式以及为什么需要使用从 Expression.Convert 返回的表达式来生成 lambda。考虑它的最佳方式是,在使用表达式树时,您必须明确所有转换,而不是考虑如何编写 C# 代码。

    【讨论】:

      【解决方案3】:

      您有Func&lt;TSource, object&gt; selector 参数。 这意味着您有函数接收TSource 对象并返回object。 所以你需要编译你的 boxingExpression 并返回结果:

           return Expression.Lambda<Func<T, object>>(boxingExpression, param).Compile();
      

      【讨论】:

      • 没错。但是当我将 Func 直接传递给 Sort 方法时,它可以工作。我不必进行明确的演员表。
      猜你喜欢
      • 1970-01-01
      • 2011-04-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-01-15
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多