要做到這一點,您必須完全重建表達式樹;參數將需要重新映射,並且現在正在與不同類型交談的所有成員訪問都需要重新應用。幸運的是,ExpressionVisitor
課程讓很多這方面變得更容易;例如(做這一切在一般情況下,不只是Func<T,bool>
謂語使用):
class TypeConversionVisitor : ExpressionVisitor
{
private readonly Dictionary<Expression, Expression> parameterMap;
public TypeConversionVisitor(
Dictionary<Expression, Expression> parameterMap)
{
this.parameterMap = parameterMap;
}
protected override Expression VisitParameter(ParameterExpression node)
{
// re-map the parameter
Expression found;
if(!parameterMap.TryGetValue(node, out found))
found = base.VisitParameter(node);
return found;
}
protected override Expression VisitMember(MemberExpression node)
{
// re-perform any member-binding
var expr = Visit(node.Expression);
if (expr.Type != node.Type)
{
MemberInfo newMember = expr.Type.GetMember(node.Member.Name)
.Single();
return Expression.MakeMemberAccess(expr, newMember);
}
return base.VisitMember(node);
}
}
在這裏,我們傳遞的參數字典來重新映射,應用在VisitParameter
。我們還在VisitMember
中檢查我們是否切換了類型(如果Visit
涉及或其他MemberExpression
,則可能發生):如果有,我們將嘗試查找另一個具有相同名稱的成員。
接下來,我們需要一種通用λ-轉換重寫方法:
// allows extension to other signatures later...
private static Expression<TTo> ConvertImpl<TFrom, TTo>(Expression<TFrom> from)
where TFrom : class
where TTo : class
{
// figure out which types are different in the function-signature
var fromTypes = from.Type.GetGenericArguments();
var toTypes = typeof(TTo).GetGenericArguments();
if (fromTypes.Length != toTypes.Length)
throw new NotSupportedException(
"Incompatible lambda function-type signatures");
Dictionary<Type, Type> typeMap = new Dictionary<Type,Type>();
for (int i = 0; i < fromTypes.Length; i++)
{
if (fromTypes[i] != toTypes[i])
typeMap[fromTypes[i]] = toTypes[i];
}
// re-map all parameters that involve different types
Dictionary<Expression, Expression> parameterMap
= new Dictionary<Expression, Expression>();
ParameterExpression[] newParams =
new ParameterExpression[from.Parameters.Count];
for (int i = 0; i < newParams.Length; i++)
{
Type newType;
if(typeMap.TryGetValue(from.Parameters[i].Type, out newType))
{
parameterMap[from.Parameters[i]] = newParams[i] =
Expression.Parameter(newType, from.Parameters[i].Name);
}
else
{
newParams[i] = from.Parameters[i];
}
}
// rebuild the lambda
var body = new TypeConversionVisitor(parameterMap).Visit(from.Body);
return Expression.Lambda<TTo>(body, newParams);
}
這取任意Expression<TFrom>
和TTo
,將其轉換爲Expression<TTo>
,通過:
- 發現這類型不同
TFrom
/TTo
- 使用該參數重新映射
- 使用表達式的遊客,我們剛剛創建
- 並最終構建一個新的lambda表達式爲所需的簽名
然後,把他們放在一起,並暴露出我們的擴展方法:
public static class Helpers {
public static Expression<Func<TTo, bool>> Convert<TFrom, TTo>(
this Expression<Func<TFrom, bool>> from)
{
return ConvertImpl<Func<TFrom, bool>, Func<TTo, bool>>(from);
}
// insert from above: ConvertImpl
// insert from above: TypeConversionVisitor
}
等瞧;通用拉姆達轉換例程,與具體實施:
Expression<Func<Test, bool>> fc2 = fc1.Convert<TestDTO, Test>();
+1爲深魔力......把你的嚮導帽子! –
@Marc Gravell:你不接受這個答案嗎? –
@金塔:我忘了回到它 - 接受自己的回答有一個延遲 –