表达式树中未发生隐式转换
我遇到了一个场景,我需要根据输入对不同属性上的自定义类型列表进行排序。在几篇文章的帮助下,我能够使用 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
您需要使用盒装版本(您当前创建
boxingExpression
,但最终查询基于propertyExpression
):Re 为什么这不是隐式 - 这里根本没有隐式转换;
表达式
!= C#。装箱是一项重要的操作,Expression
API 需要树中的特定节点。You need to use the boxed version (you currently create
boxingExpression
, but base your final query instead onpropertyExpression
):Re why this isn't implicit - there simply is no implicit casting here;
Expression
!= C#. Boxing is a non-trivial operation, and theExpression
API requires a specific node in the tree.您将 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.
您有
Func;选择器
参数。这意味着您有函数接收
TSource
对象并返回object
。因此,您需要编译 boxingExpression 并返回结果:
You have the
Func<TSource, object> selector
parameter.It means that you have function recieves the
TSource
object and returnsobject
.So you need to compile your boxingExpression and return the result: