无法使用 List<T> 手动创建 Lambda 表达式树
Unable to manually create Lambda expression tree with List<T>
我需要帮助手动创建此 lambda 表达式。我无法通过正确创建 MemberExpression
来隔离底层 List<Dog>
的“Breed
”属性。在这个例子中,Breed
是 Dog
的 属性。
这是我需要手动创建的lambda:
int maxlen = dogList.Select(d => d.Breed.Trim().Length).OrderByDescending(d1 => d1).First();
这是我尝试获取 Breed
属性:
ParameterExpression dogParam = Expression.Parameter(typeof(List<Dog>), "dog");
MemberExpression dogMember =
Expression.Field(dogParam, dogMember.Type.GetGenericTypeDefinition().GetProperty("Breed"));
我不确定你的问题到底出在哪里,但它已经帮助你找到你最可能需要的 API:
您可以使用 Expression.Lambda
创建一个 lambda 表达式作为 Select
的参数。在 lambda 表达式中,您需要 Expression.MakeMemberAccess
才能访问 Breed
属性。您需要反射方法(例如 Type.GetMember
)来获取相关的 MemberInfo
对象。
这是一个痛苦,一个真正的痛苦。
首先注意你可以作弊,看看编译器生成了什么:
Expression<Func<List<Dog>, int>> exp = dogList => dogList.Select(d => d.Breed.Trim().Length).OrderByDescending(d1 => d1).First();
遗憾的是,这并不是一个很大的帮助...您有两个“内部”lambda 表达式(Select
和 OrderByDescending
)和三个通用方法(Select
, OrderByDescending
, First
)...一个痛苦.
我已经注释了所有的代码行,所以应该很清楚每行的作用。
// The dogList parameter
ParameterExpression dogParam = Expression.Parameter(typeof(List<Dog>), "dog");
// Begin of inner Select expression
// d parameter
ParameterExpression dParam = Expression.Parameter(typeof(Dog), "d");
// d.Breed *property* access (if it is a *field*, use Expression.Field)
MemberExpression dogProperty = Expression.Property(dParam, nameof(Dog.Breed));
// d.Breed.Trim() method call
MethodCallExpression trimCall = Expression.Call(dogProperty, nameof(string.Trim), Type.EmptyTypes);
// d.Breed.Trim().Length property access
MemberExpression lengthProperty = Expression.Property(trimCall, nameof(string.Length));
// d => d.Breed.Trim().Length
Expression<Func<Dog, int>> selectExpression = Expression.Lambda<Func<Dog, int>>(lengthProperty, dParam);
// End of inner Select expression
// Find the "right" overload of Enumerable.Select... A pain.
MethodInfo selectTSourceTResult = (from x in typeof(Enumerable).GetMethods(BindingFlags.Static | BindingFlags.Public)
where x.Name == nameof(Enumerable.Select)
let args = x.GetGenericArguments()
where args.Length == 2
let pars = x.GetParameters()
where pars.Length == 2 &&
pars[0].ParameterType == typeof(IEnumerable<>).MakeGenericType(args[0]) &&
pars[1].ParameterType == typeof(Func<,>).MakeGenericType(args[0], args[1])
select x).Single();
// Make the generic overload of Enumerable.Select "specific" for using with Func<Dog, int>
MethodInfo selectDogInt32 = selectTSourceTResult.MakeGenericMethod(typeof(Dog), typeof(int));
// Note that Enumerable.Select is a static method, the first parameter is the IEnumerable, the second is the Func<,>
// Enumerable.Select(dogList, d => d.Breed.Trim().Length)
MethodCallExpression selectCall = Expression.Call(selectDogInt32, dogParam, selectExpression);
// Begin of inner OrderByDescending expression
// d1 parameter
ParameterExpression d1Param = Expression.Parameter(typeof(int), "d1");
// d1 => d1
Expression<Func<int, int>> orderByExpression = Expression.Lambda<Func<int, int>>(d1Param, d1Param);
// End of inner OrderByDescending expression
// Find the "right" overload of Enumerable.OrderByDescending... Another pain.
MethodInfo orderByDescendingTSourceTKey = (from x in typeof(Enumerable).GetMethods(BindingFlags.Static | BindingFlags.Public)
where x.Name == nameof(Enumerable.OrderByDescending)
let args = x.GetGenericArguments()
where args.Length == 2
let pars = x.GetParameters()
where pars.Length == 2 &&
pars[0].ParameterType == typeof(IEnumerable<>).MakeGenericType(args[0]) &&
pars[1].ParameterType == typeof(Func<,>).MakeGenericType(args[0], args[1])
select x).Single();
// Make the generic overload of Enumerable.OrderByDescending "specific" for using with Func<int, int>
MethodInfo orderByDescendingInt32Int32 = orderByDescendingTSourceTKey.MakeGenericMethod(typeof(int), typeof(int));
// Note that Enumerable.OrderByDescending is a static method, the first parameter is the IEnumerable, the second is the Func<,>
// Enumerable.OrderByDescending(Enumerable.Select(...), d1 => d1)
MethodCallExpression orderByDescendingCall = Expression.Call(orderByDescendingInt32Int32, selectCall, orderByExpression);
// Find the "right" overload of Enumerable.First... Third big pain.
MethodInfo firstTSource = (from x in typeof(Enumerable).GetMethods(BindingFlags.Static | BindingFlags.Public)
where x.Name == nameof(Enumerable.First)
let args = x.GetGenericArguments()
where args.Length == 1
let pars = x.GetParameters()
where pars.Length == 1 &&
pars[0].ParameterType == typeof(IEnumerable<>).MakeGenericType(args[0])
select x).Single();
// Make the generic overload of Enumerable.OrderByDescending "specific" for using with int
MethodInfo firstInt32 = firstTSource.MakeGenericMethod(typeof(int));
// Note that Enumerable.First is a static method, the first parameter is the IEnumerable
// Enumerable.First(Enumerable.OrderByDescending(...))
MethodCallExpression firstCall = Expression.Call(firstInt32, orderByDescendingCall);
我需要帮助手动创建此 lambda 表达式。我无法通过正确创建 MemberExpression
来隔离底层 List<Dog>
的“Breed
”属性。在这个例子中,Breed
是 Dog
的 属性。
这是我需要手动创建的lambda:
int maxlen = dogList.Select(d => d.Breed.Trim().Length).OrderByDescending(d1 => d1).First();
这是我尝试获取 Breed
属性:
ParameterExpression dogParam = Expression.Parameter(typeof(List<Dog>), "dog");
MemberExpression dogMember =
Expression.Field(dogParam, dogMember.Type.GetGenericTypeDefinition().GetProperty("Breed"));
我不确定你的问题到底出在哪里,但它已经帮助你找到你最可能需要的 API:
您可以使用 Expression.Lambda
创建一个 lambda 表达式作为 Select
的参数。在 lambda 表达式中,您需要 Expression.MakeMemberAccess
才能访问 Breed
属性。您需要反射方法(例如 Type.GetMember
)来获取相关的 MemberInfo
对象。
这是一个痛苦,一个真正的痛苦。
首先注意你可以作弊,看看编译器生成了什么:
Expression<Func<List<Dog>, int>> exp = dogList => dogList.Select(d => d.Breed.Trim().Length).OrderByDescending(d1 => d1).First();
遗憾的是,这并不是一个很大的帮助...您有两个“内部”lambda 表达式(Select
和 OrderByDescending
)和三个通用方法(Select
, OrderByDescending
, First
)...一个痛苦.
我已经注释了所有的代码行,所以应该很清楚每行的作用。
// The dogList parameter
ParameterExpression dogParam = Expression.Parameter(typeof(List<Dog>), "dog");
// Begin of inner Select expression
// d parameter
ParameterExpression dParam = Expression.Parameter(typeof(Dog), "d");
// d.Breed *property* access (if it is a *field*, use Expression.Field)
MemberExpression dogProperty = Expression.Property(dParam, nameof(Dog.Breed));
// d.Breed.Trim() method call
MethodCallExpression trimCall = Expression.Call(dogProperty, nameof(string.Trim), Type.EmptyTypes);
// d.Breed.Trim().Length property access
MemberExpression lengthProperty = Expression.Property(trimCall, nameof(string.Length));
// d => d.Breed.Trim().Length
Expression<Func<Dog, int>> selectExpression = Expression.Lambda<Func<Dog, int>>(lengthProperty, dParam);
// End of inner Select expression
// Find the "right" overload of Enumerable.Select... A pain.
MethodInfo selectTSourceTResult = (from x in typeof(Enumerable).GetMethods(BindingFlags.Static | BindingFlags.Public)
where x.Name == nameof(Enumerable.Select)
let args = x.GetGenericArguments()
where args.Length == 2
let pars = x.GetParameters()
where pars.Length == 2 &&
pars[0].ParameterType == typeof(IEnumerable<>).MakeGenericType(args[0]) &&
pars[1].ParameterType == typeof(Func<,>).MakeGenericType(args[0], args[1])
select x).Single();
// Make the generic overload of Enumerable.Select "specific" for using with Func<Dog, int>
MethodInfo selectDogInt32 = selectTSourceTResult.MakeGenericMethod(typeof(Dog), typeof(int));
// Note that Enumerable.Select is a static method, the first parameter is the IEnumerable, the second is the Func<,>
// Enumerable.Select(dogList, d => d.Breed.Trim().Length)
MethodCallExpression selectCall = Expression.Call(selectDogInt32, dogParam, selectExpression);
// Begin of inner OrderByDescending expression
// d1 parameter
ParameterExpression d1Param = Expression.Parameter(typeof(int), "d1");
// d1 => d1
Expression<Func<int, int>> orderByExpression = Expression.Lambda<Func<int, int>>(d1Param, d1Param);
// End of inner OrderByDescending expression
// Find the "right" overload of Enumerable.OrderByDescending... Another pain.
MethodInfo orderByDescendingTSourceTKey = (from x in typeof(Enumerable).GetMethods(BindingFlags.Static | BindingFlags.Public)
where x.Name == nameof(Enumerable.OrderByDescending)
let args = x.GetGenericArguments()
where args.Length == 2
let pars = x.GetParameters()
where pars.Length == 2 &&
pars[0].ParameterType == typeof(IEnumerable<>).MakeGenericType(args[0]) &&
pars[1].ParameterType == typeof(Func<,>).MakeGenericType(args[0], args[1])
select x).Single();
// Make the generic overload of Enumerable.OrderByDescending "specific" for using with Func<int, int>
MethodInfo orderByDescendingInt32Int32 = orderByDescendingTSourceTKey.MakeGenericMethod(typeof(int), typeof(int));
// Note that Enumerable.OrderByDescending is a static method, the first parameter is the IEnumerable, the second is the Func<,>
// Enumerable.OrderByDescending(Enumerable.Select(...), d1 => d1)
MethodCallExpression orderByDescendingCall = Expression.Call(orderByDescendingInt32Int32, selectCall, orderByExpression);
// Find the "right" overload of Enumerable.First... Third big pain.
MethodInfo firstTSource = (from x in typeof(Enumerable).GetMethods(BindingFlags.Static | BindingFlags.Public)
where x.Name == nameof(Enumerable.First)
let args = x.GetGenericArguments()
where args.Length == 1
let pars = x.GetParameters()
where pars.Length == 1 &&
pars[0].ParameterType == typeof(IEnumerable<>).MakeGenericType(args[0])
select x).Single();
// Make the generic overload of Enumerable.OrderByDescending "specific" for using with int
MethodInfo firstInt32 = firstTSource.MakeGenericMethod(typeof(int));
// Note that Enumerable.First is a static method, the first parameter is the IEnumerable
// Enumerable.First(Enumerable.OrderByDescending(...))
MethodCallExpression firstCall = Expression.Call(firstInt32, orderByDescendingCall);