gpt4 book ai didi

c# - 如何在不同(但兼容)模型之间转换 lambda 表达式?

转载 作者:IT王子 更新时间:2023-10-29 04:49:01 25 4
gpt4 key购买 nike

(基于电子邮件对话,现记录下来以供信息共享)我有两个模型用于不同的层:

public class TestDTO {
public int CustomerID { get; set; }
}
//...
public class Test {
public int CustomerID { get; set; }
}

和我的 DTO 层的 lambda:

Expression<Func<TestDTO, bool>> fc1 =
(TestDTO c1) => c1.CustomerID <= 100 && c1.CustomerID >= 10;

我如何将该 lambda(在一般情况下)转换为谈论其他模型:

Expression<Func<Test, bool>> fc2 = {insert magic here, based on fc1}

(显然,我们在相同的测试条件之后,但使用的是 Test 类型)

?

最佳答案

为此,您必须完全重建表达式树;参数将需要重新映射,并且现在与不同类型通信的所有成员访问都需要重新应用。幸运的是,ExpressionVisitor 使很多事情变得更容易类(class);例如(在一般情况下全部执行,而不仅仅是 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 涉及一个 ParameterExpression 或另一个 MemberExpression ,在任何时候都可能发生):如果我们有,我们将尝试找到另一个同名成员.

接下来,我们需要一个通用的 lambda 转换重写器方法:

// 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
}

等瞧;一个通用的 lambda 转换例程,具有以下具体实现:

Expression<Func<Test, bool>> fc2 = fc1.Convert<TestDTO, Test>();

关于c# - 如何在不同(但兼容)模型之间转换 lambda 表达式?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14007101/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com