Implicit Cast not happening in Expression Tree

老子叫甜甜 提交于 2021-02-08 13:56:49

问题


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.


回答1:


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.




回答2:


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.




回答3:


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


来源:https://stackoverflow.com/questions/6187664/implicit-cast-not-happening-in-expression-tree

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!