gpt4 book ai didi

c# - 替换参数以指向 lambda 表达式中的嵌套参数

转载 作者:太空狗 更新时间:2023-10-29 18:26:36 25 4
gpt4 key购买 nike

感谢 previous question 上的一些答案,我可以成功地替换 lambda 表达式中的简单参数类型但我不知道如何将参数从传入的 lambda 替换为嵌套参数。

考虑以下对象:

public class DtoColour {

public DtoColour(string name)
{
Name = name;
}

public string Name { get; set; }

public ICollection<DtoFavouriteColour> FavouriteColours { get; set; }
}

public class DtoPerson
{
public DtoPerson(string firstName, string lastName)
{
FirstName = firstName;
LastName = lastName;
FavouriteColours = new Collection<DtoFavouriteColour>();
}

public string FirstName { get; private set; }

public string LastName { get; private set; }

public ICollection<DtoFavouriteColour> FavouriteColours { get; set; }
}

public class DtoFavouriteColour
{
public DtoColour Colour { get; set; }

public DtoPerson Person { get; set; }
}

public class DomainColour {

public DomainColour(string name)
{
Name = name;
}

public string Name { get; set; }

public ICollection<DomainPerson> People { get; set; }
}

public class DomainPerson {

public DomainPerson(string firstName, string lastName)
{
FirstName = firstName;
LastName = lastName;
Colours = new Collection<DomainColour>();
}

public string FirstName { get; private set; }

public string LastName { get; private set; }

public ICollection<DomainColour> Colours { get; set; }
}

和一个存储库:

public class ColourRepository {

private IList<DtoColour> Colours { get; set; }

public ColourRepository()
{
var favColours = new Collection<DtoFavouriteColour>
{
new DtoFavouriteColour() { Person = new DtoPerson("Peter", "Parker") },
new DtoFavouriteColour() { Person = new DtoPerson("John", "Smith") },
new DtoFavouriteColour() { Person = new DtoPerson("Joe", "Blogs") }
};
Colours = new List<DtoColour>
{
new DtoColour("Red") { FavouriteColours = favColours },
new DtoColour("Blue"),
new DtoColour("Yellow")
};
}

public IEnumerable<DomainColour> GetWhere(Expression<Func<DomainColour, bool>> predicate)
{
var coonvertedPred = MyExpressionVisitor.Convert(predicate);
return Colours.Where(coonvertedPred).Select(c => new DomainColour(c.Name)).ToList();
}
}

最后是一个表达式访问者,它将谓词转换为 Dto 模型的正确谓词

public class MyExpressionVisitor : ExpressionVisitor
{
private ReadOnlyCollection<ParameterExpression> _parameters;

public static Func<DtoColour, bool> Convert<T>(Expression<T> root)
{
var visitor = new MyExpressionVisitor();
var expression = (Expression<Func<DtoColour, bool>>)visitor.Visit(root);
return expression.Compile();
}

protected override Expression VisitParameter(ParameterExpression node)
{
var param = _parameters?.FirstOrDefault(p => p.Name == node.Name);

if (param != null)
{
return param;
}

if(node.Type == typeof(DomainColour))
{
return Expression.Parameter(typeof(DtoColour), node.Name);
}

if (node.Type == typeof(DomainPerson))
{
return Expression.Parameter(typeof(DtoFavouriteColour), node.Name);
}

return node;
}

protected override Expression VisitLambda<T>(Expression<T> node)
{
_parameters = VisitAndConvert<ParameterExpression>(node.Parameters, "VisitLambda");
return Expression.Lambda(Visit(node.Body), _parameters);
}

protected override Expression VisitMember(MemberExpression node)
{
var exp = Visit(node.Expression);

if (node.Member.DeclaringType == typeof(DomainColour))
{
if (node.Type == typeof(ICollection<DomainPerson>))
{
return Expression.MakeMemberAccess(exp, typeof(DtoColour).GetProperty("FavouriteColours"));
}

return Expression.MakeMemberAccess(exp, typeof(DtoColour).GetProperty(node.Member.Name));
}

if (node.Member.DeclaringType == typeof(DomainPerson))
{
var nested = Expression.MakeMemberAccess(exp, typeof(DtoFavouriteColour).GetProperty("Person"));
return Expression.MakeMemberAccess(nested, typeof(DtoPerson).GetProperty(node.Member.Name));
}

return base.VisitMember(node);
}
}

目前我得到以下异常

[System.ArgumentException: Expression of type 'System.Collections.Generic.ICollection1[ExpressionVisitorTests.DtoFavouriteColour]'
cannot be used for parameter of type
'System.Collections.Generic.IEnumerable
1[ExpressionVisitorTests.DomainPerson]' of method 'Boolean Any[DomainPerson](System.Collections.Generic.IEnumerable1[ExpressionVisitorTests.DomainPerson],
System.Func
2[ExpressionVisitorTests.DomainPerson,System.Boolean])']

这是一个dotnetfiddle它不起作用。

提前感谢您的帮助。

最佳答案

经过更多搜索,我找到了 this answer通过 John Skeet这让我想出了一个可行的解决方案,其中包括为 VisitMethodCall 添加一个覆盖。 ExpressionVisitor 上的方法替换原来的MethodInfo为正确的集合类型使用一个新的。

protected override Expression VisitMethodCall(MethodCallExpression node)
{
if (node.Method.DeclaringType == typeof(Enumerable) && node.Arguments[0].Type == typeof(ICollection<DomainPerson>))
{
Expression obj = Visit(node.Object);
IEnumerable<Expression> args = Visit(node.Arguments);
if (obj != node.Object || args != node.Arguments)
{
var generic = typeof(Enumerable).GetMethods()
.Where(m => m.Name == node.Method.Name)
.Where(m => m.GetParameters().Length == node.Arguments.Count)
.Single();
var constructed = generic.MakeGenericMethod(typeof(DtoFavouriteColour));
return Expression.Call(obj, constructed, args);
}
}
return node;
}

我还需要确保我对 _parameters 的引用集合未被对 VisitLambda<T> 的嵌套调用所取代这可能在访问 node.Body 时发生.

protected override Expression VisitLambda<T>(Expression<T> node)
{
var parameters = VisitAndConvert(node.Parameters, "VisitLambda");

// ensure parameters set but dont let original reference
// be overidden by nested calls
_parameters = parameters;

return Expression.Lambda(Visit(node.Body), parameters);
}

参见 dotnetfiddle用于完全工作的解决方案。

如果有人有更好/更优雅的解决方案请添加答案让我标记。

关于c# - 替换参数以指向 lambda 表达式中的嵌套参数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38499265/

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