我遇到了一个场景,我需要根据输入对不同属性的自定义类型列表进行排序。在几篇文章的帮助下,我能够使用LINQ.Auring单元测试来提出通用实现,其中一个测试失败,因为当使用Expression树创建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扩展方法时,它可以工作但是它失败了Expression树。
我发现了一个similar问题,但这涉及到约束类型参数。问题是否相同?有人可以帮助我理解这个问题。
答案 0 :(得分:5)
您需要使用盒装版本(目前创建 boxingExpression
,但最终查询的基础是propertyExpression
):
return Expression.Lambda<Func<T, object>>(boxingExpression, param).Compile();
为什么这不是隐含的 - 只有 这里没有隐式转换; Expression
!= C#。 Boxing是一项非常重要的操作,Expression
API需要树中的特定节点。
答案 1 :(得分:2)
您将 GetSortFunc 原型作为返回 Func&lt;&gt; 实例的原型返回一个对象。因此,确保您生成的表达式树产生一个对象是您的工作。
虽然 int 隐含地转换为C#中的对象,但它正在装箱。这就是为什么你需要在代码中使用装箱表达式以及为什么需要使用从 Expression.Convert 返回的表达式生成lambda。考虑它的最佳方式是,在使用表达式树时,您必须明确所有转换,而不要考虑如何编写C#代码。
答案 2 :(得分:1)
您拥有Func<TSource, object> selector
参数。
这意味着您有函数接收TSource
对象并返回object
。
所以你需要编译拳击表达式并返回结果:
return Expression.Lambda<Func<T, object>>(boxingExpression, param).Compile();