拥有模特:
public class MyCustomObject
{
public int Id { get; set; }
public string Description { get; set; }
//A lot of properties
}
我需要通过自定义属性订购的查询queryableObjectsToOrder
。
属性名称存储为字符串,可以是"Id"
或"Description"
。
queryableObjectsToOrder.OrderBy(customSortCreatedInRuntime);
然后我创建了一个通用方法来构建Func to Order。
public Func<TSource, TResult> CreateCustomSort<TSource, TResult>(Type t, string fieldName)
{
var propertyField = t.GetProperty(fieldName);
if (propertyField != null)
{
var parameterExp = Expression.Parameter(t, "sel");
var fieldProp = Expression.PropertyOrField(parameterExp, fieldName);
var lambda = Expression.Lambda<Func<TSource, TResult>>(fieldProp, parameterExp);
return lambda.Compile();
}
else
return (obj) => { return default(TResult); };
}
我称之为并且有效:
string propertyToOrder = "Id";
var customSortCreatedInRuntime = CreateCustomSort<MyCustomObject, int>(typeof(MyCustomObject), propertyToOrder);
var resultList = queryableObjectsToOrder.OrderBy(customSortCreatedInRuntime);
但是如果我将字符串propertyToOrder更改为&#34;描述&#34;,我需要将对通用方法的调用的签名更改为:
string propertyToOrder = "Description";
var customSortCreatedInRuntime = CreateCustomSort<MyCustomObject, string>(typeof(MyCustomObject), propertyToOrder);
这是一个问题,因为我不知道存储为字符串的属性名称的类型。 如何在不指定类型TResult的情况下重写CreateCustomSort方法?
答案 0 :(得分:1)
编译器无法通过运行时可用的信息(在您的情况下,string
参数)静态验证类型安全表达式(在您的情况下是通用返回类型)。
但如果适合您,可以将其设为dynamic
。
答案 1 :(得分:1)
当我看到这样的问题时,我总是想知道:&#34;为什么?&#34;和&#34;这闻起来像XY Problem&#34;。什么阻止你做常规的类型安全lambda?
无论如何,最简单的方法是不构建lambda:
public Func<TSource, object> CreateCustomSort<TSource>(string fieldName)
{
var prop = TypeDescriptor.GetProperties(typeof(TSource)).Find(fieldName, false);
if (prop != null)
return (x) => prop.GetValue(x, null);
else
return (x) => null;
}
如果要使用与手动键入的lambda表达式完全等效,则需要执行更复杂的操作,例如构建包含IQueryable
表达式的新OrderBy
实例。以下扩展方法执行此操作:
public static class OrderByExtensions
{
public static IQueryable<TSource> OrderByField<TSource>(this IQueryable<TSource> query, string fieldName, bool isAscending = true)
{
var prop = TypeDescriptor.GetProperties(typeof(TSource)).Find(fieldName, false);
if (prop == null)
return query;
var sourceExpr = Expression.Parameter(typeof(TSource), "source");
var propExpr = Expression.Property(sourceExpr, prop.Name);
var selectorExpr = Expression.Lambda(propExpr, sourceExpr);
string method = isAscending ? "OrderBy" : "OrderByDescending";
Type[] types = new Type[] { query.ElementType, selectorExpr.Body.Type };
var orderByCallExpr = Expression.Call(typeof(Queryable), method, types, query.Expression, selectorExpr);
return query.Provider.CreateQuery<TSource>(orderByCallExpr);
}
}
用法示例:
public static void Main(params string[] args)
{
var myCustomObjects = new[] {
new MyCustomObject() { Id = 10, Description = "Hello" },
new MyCustomObject() { Id = 2, Description = "SO" },
new MyCustomObject() { Id = 42, Description = "Abcde" }
};
var result = myCustomObjects
.AsQueryable()
.OrderByField("Description");
foreach (var r in result)
Console.WriteLine("{0} - {1}", r.Id, r.Description);
}
输出:
42 - Abcde
10 - Hello
2 - SO
注意:强>
构建这些查询表达式是很昂贵的,如果你想看到这个更复杂的代码段的任何性能优势,你应该按照TSource
(并且有很多调用)将构建的表达式缓存在缓存中。