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

发布于 2024-11-10 20:59:19 字数 2336 浏览 2 评论 0原文

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

我发现了 类似问题,但讨论的是受约束的类型参数。两个问题是一样的吗?有人可以帮助我理解这个问题吗?

I came across a scenario where I need to sort a list of custom type on different properties based on input. With the help of few articles, I was able to come up with generic implementation using LINQ.During unit testing, one of the test failed because implicit conversion was happening when lamda expression was created using Expression tree.

Below I have put the sample code to understand the issue (Not sure why formatting was not getting correct, sorry for that)

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

    }
} 

In the Main method, when I try to pass a Func delegate directly to Sort extension method it works but it fails with Expression tree.

I found a similar issue, but that talks about constrained type parameters. Is both the issues same? Can somebody help me understand the issue.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

一城柳絮吹成雪 2024-11-17 20:59:19

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

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

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

You need to use the boxed version (you currently create boxingExpression, but base your final query instead on propertyExpression):

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

Re why this isn't implicit - there simply is no implicit casting here; Expression != C#. Boxing is a non-trivial operation, and the Expression API requires a specific node in the tree.

讽刺将军 2024-11-17 20:59:19

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

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

You've prototypes GetSortFunc as returning a Func<> instance which returns an object. Because of this it's your job to ensure that the expression tree you generate yields an object.

Although int in implicitly convert to an object in C# under the hood it's being boxed. That's why you need the boxing expression in your code and why you need to generate the lambda using the expression you get back from Expression.Convert. The best way to think about it is that when using expression trees you have to be explicit about all conversions and not think of it in terms of how you'd write the C# code.

时光瘦了 2024-11-17 20:59:19

您有 Func;选择器参数。
这意味着您有函数接收 TSource 对象并返回 object
因此,您需要编译 boxingExpression 并返回结果:

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

You have the Func<TSource, object> selector parameter.
It means that you have function recieves the TSource object and returns object.
So you need to compile your boxingExpression and return the result:

     return Expression.Lambda<Func<T, object>>(boxingExpression, param).Compile();
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文