表达式树中未发生隐式强制转换

本文关键字:转换 表达式 | 更新日期: 2023-09-27 17:57:58

我遇到了一个场景,需要根据输入对不同属性的自定义类型列表进行排序。在几篇文章的帮助下,我能够提出使用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扩展方法时,它可以工作,但在表达式树中失败了。

我发现了一个类似的问题,但它讨论了受约束的类型参数。这两个问题相同吗?有人能帮我理解这个问题吗。

表达式树中未发生隐式强制转换

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

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

关于为什么这不是隐式的-这里只是没有隐式铸造;Expression!=C#。装箱是一个非平凡的操作,Expression API需要树中的一个特定节点。

您的原型GetSortFunc返回Func<>实例,返回一个对象。因此,您的工作就是确保生成的表达式树生成一个对象。

尽管中的int在C#中隐式转换为对象,但它被装箱了。这就是为什么你需要在代码中使用装箱表达式,以及为什么你需要使用从expression.Convert返回的表达式来生成lambda。最好的方法是,当使用表达式树时,你必须明确所有的转换,而不是从如何编写C#代码的角度来考虑它。

您有Func<TSource, object> selector参数。这意味着您的函数接收TSource对象并返回object。因此,您需要编译boxingExpression并返回结果:

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