gpt4 book ai didi

c# - 表达式几乎相同,但其中一个不起作用

转载 作者:行者123 更新时间:2023-11-30 16:45:27 26 4
gpt4 key购买 nike

表达式 并不完全相同,但应该相同。它们在一个细微的细节上有所不同。我对 Expressions 很陌生,但我认为即使对于经验丰富的玩家来说,这也可能会造成混淆。我重构了处理一些数据的代码,使 Expression 用作 IQueryable.Where() 的参数。据我所知,它在功能上是等效的。

我这里有以前的代码,它运行良好,并生成完美的功能表达式:

private Expression<Func<T, bool>> StringPropertyContains<T>(string propertyName, string value)
{
if (string.IsNullOrWhiteSpace(propertyName))
{
throw new ArgumentNullException(nameof(propertyName));
}

var param = Expression.Parameter(typeof(T));

MemberExpression member = null;
if (propertyName.Contains('/'))
{
var splittedPropertyName = propertyName.Split('/');

var propertyInfo = this.GetPropertyInfo(typeof(T), splittedPropertyName.First());
member = Expression.MakeMemberAccess(param, propertyInfo);

for (int i = 1; i < splittedPropertyName.Length; i++)
{
if (propertyInfo.PropertyType.IsInterface)
{
//specifically for IActorWithExtraDetails -> reason to refactor
if (typeof(IActor).IsAssignableFrom(propertyInfo.PropertyType) && typeof(IActor).GetProperties().FirstOrDefault(pi => pi.Name.Equals(splittedPropertyName[i], StringComparison.OrdinalIgnoreCase)) != null)
{
propertyInfo = this.GetPropertyInfo(typeof(IActor), splittedPropertyName[i]);
}
else
{
propertyInfo = this.GetPropertyInfo(propertyInfo.PropertyType, splittedPropertyName[i]);
}
}
else
{
propertyInfo = this.GetPropertyInfo(propertyInfo.PropertyType, splittedPropertyName[i]);
}

}

member = Expression.MakeMemberAccess(member, propertyInfo);
}
else
{
var propertyInfo = this.GetPropertyInfo(typeof(T), propertyName);

member = Expression.MakeMemberAccess(param, propertyInfo);
}

var constant = Expression.Constant(value, typeof(string));
var methodInfo = typeof(string).GetMethod("Contains", new Type[] { typeof(string) });
var body = Expression.Call(member, methodInfo, constant);

return Expression.Lambda<Func<T, bool>>(body, param);
}

这是它在 IQueryableDebugView 属性中的样子:

.Lambda #Lambda2<System.Func`2[AccessManagement.Model.Application,System.Boolean]>(AccessManagement.Model.Application $var1)
{
.Call ($var1.Name).Contains("hive")
}

这里是新的、重构的代码,移到了自己的方法中:

private Expression<Func<T, bool>> StringPropertyContains<T>(string propertyName, string value)
{
if (string.IsNullOrWhiteSpace(propertyName))
{
throw new ArgumentNullException(nameof(propertyName));
}

var param = Expression.Parameter(typeof(T));

MemberExpression member = this.GetMemberExpression(typeof(T), propertyName.Trim('/').Split('/'));

var constant = Expression.Constant(value, typeof(string));
var methodInfo = typeof(string).GetMethod("Contains", new Type[] { typeof(string) });
var body = Expression.Call(member, methodInfo, constant);

return Expression.Lambda<Func<T, bool>>(body, param);
}

private MemberExpression GetMemberExpression(Type baseType, string[] path)
{
MemberExpression result = null;
Type type = baseType;
PropertyInfo propertyInfo = null;

foreach (string segment in path)
{
//if type is interface, just spray and pray
if (type.IsInterface)
{
propertyInfo = this.GetDescendantProperties(type)
.FirstOrDefault(pi => pi.Name.Equals(segment, StringComparison.OrdinalIgnoreCase));
}
else
{
propertyInfo = this.GetPropertyInfo(type, segment);
}

if (propertyInfo == null)
{
throw new ArgumentNullException(nameof(propertyInfo));
}

result =
result == null ?
Expression.MakeMemberAccess(Expression.Parameter(baseType), propertyInfo) :
Expression.MakeMemberAccess(result, propertyInfo);
}

return result;
}

这是 DebugView 中重构方法的表达式:

.Lambda #Lambda2<System.Func`2[AccessManagement.Model.Application,System.Boolean]>(AccessManagement.Model.Application $var1)
{
.Call ($var2.Name).Contains("hive")
}

只有一处不同。如您所见,在第二种情况下有 $var2,而不是 $var1。该变量不存在于整个表达式树中。我不知道为什么,但我敢打赌这就是问题所在,因为其他一切都保持不变。唯一的不同是,在第二种情况下,Expression 处理是缓存在 member.RuntimeMethodInfo.base.m_cachedData(调试 View 路径)中的内容。

最佳答案

在您的第一个代码片段中,您声明了一个参数:

var param = Expression.Parameter(typeof(T));

这用作 lambda 参数,并在此处的代码中使用:

 Expression.MakeMemberAccess(param, propertyInfo);

(这实际上被调用了两次)。因此代码使用传递给 lambda 的参数。


在你的第二个代码片段中,你仍然使用 param 作为 lambda 的参数,但是你没有在 lambda 主体的任何地方使用它。

你正在调用它:

Expression.MakeMemberAccess(Expression.Parameter(baseType), propertyInfo)

Expression.Parameter(baseType) 创建了第二个不相关 变量,它从未获得分配给它的实际值。您应该在此处使用 param 引用。

这就是 $var2 的来源。 $var1param 引用。

下次考虑使用 Expression.Parameter(Type, string) 重载,它允许您命名参数以用于调试目的。推理起来会更容易。

关于c# - 表达式几乎相同,但其中一个不起作用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42132589/

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