我真的很难写一个泛型类,需要通过调用实际对象来检查传递表达式的所有成员是否都为null。
我正在调用方法:
new TestClass<Class1, Class2>.IsAnyMemberNull(x => x.Property1.List1, object1);
,方法如下:
public bool IsAnyMemberNull(Expression<Func<T, IList<TM>>> listExpression, T entityDb)
{
var expressionsToCheck = new List<MemberExpression>();
var expression = listExpression.Body as MemberExpression;
while (expression != null)
{
expressionsToCheck.Add(expression);
expression = expression.Expression as MemberExpression;
}
for (var i = expressionsToCheck.Count - 1; i >= 0; i--)
{
var objectMember = Expression.Convert(expressionsToCheck[i], typeof (object));
var lambda = Expression.Lambda<Func<T, object>>(objectMember);
var value = lambda.Compile().Invoke(entityDb);
if (value == null)
return true;
}
return false;
}
执行时,我得到例外:
为lambda声明提供的参数数量不正确
任何想法,我做错了什么?
答案 0 :(得分:1)
虽然可以使代码正常工作,但是构建和编译正确的lambdas,使用重复编译的lambdas来实现空值检查是一种代价高昂的过度杀伤。
通常,如此随意地使用lambdas(为链中的每个属性和单个对象编译lambda)会带来显着的性能影响。我已经运行了一些测试,并且在我的计算机上执行此方法1000次,对于给定的对象产生~300-700毫秒的时间(取决于链中的属性数)。 Dunno您处理了多少实体,但这不是一个好兆头,并且可以提供更好的替代品。请进一步阅读......
问题是,你用它做什么?你的那种方法让我想起了null-conditional operators。一般来说,如果你:
x => x.Property1.List1
)在运行时已知然后你可以完全取消所有IsAnyMemberNull方法,以支持以下内容:
object1.Property1.List1 == null
更简洁,无需其他方法。我运行了100万次,它仍然在~23ms的时间内。这意味着它比创建所有这些lambdas快几十万。
如果由于某种原因无法使用null-coalescing运算符(特别是在动态构建表达式时),您可能会决定使用Field / Property反射。
我冒昧地删除所有泛型类包装,转而使用泛型方法。从您的用法示例来看,通用类的唯一目的似乎是使用类'泛型类型参数访问特定方法。这意味着必须为方法的每个变体制作和存储一个新类,没有明显的理由,如果我没有弄错的话,应用程序的剩余时间。在这类情况下,特定类中的泛型方法通常优先于泛型类中的特定方法。
另外,我删除了IList,因为我认为没有理由要求最后一个参数是IList类型服务于函数的目的;它只会限制其适用性而没有明显的收益。
总体而言,结果如下:
public bool IsAnyMemberNull<TEntity, TMember>(Expression<Func<TEntity, TMember>> paramChain, TEntity entityDb)
{
var expressionsToCheck = new List<MemberExpression>();
var expression = paramChain.Body as MemberExpression;
while (expression != null)
{
expressionsToCheck.Add(expression);
expression = expression.Expression as MemberExpression;
}
object value = entityDb;
for (var i = expressionsToCheck.Count - 1; i >= 0; i--)
{
var member = expressionsToCheck[i].Member;
if (member is PropertyInfo) value = (member as PropertyInfo).GetValue(value);
else if (member is FieldInfo) value = (member as FieldInfo).GetValue(value);
else throw new Exception(); // member generally should be a property or field, shouldn't it?
if (value == null)
return true;
}
return false;
}
运行~1000次后,耗时约4-6ms;比lambdas好50-100倍,尽管无效传播仍然占主导地位。
调用如下(假设它仍然驻留在TestClass中,它不需要):
new TestClass().IsAnyMemberNull<Class1,Class2>(x => x.Property1.List1, object1);
(由于类型推断,可能不需要Class1和Class2)
希望这会有所帮助。这不完全是你要求的,但我担心所有这些lambda产生你会遇到严重的性能问题;特别是如果每个请求多次使用此代码。
答案 1 :(得分:0)
你在lambda表达式创建时遇到问题 - 它比你想象的要简单。您应该使用原始表达式参数为每个lambda
构建expressionToCheck
:
for (var i = expressionsToCheck.Count - 1; i >= 0; i--)
{
var lambda = Expression.Lambda<Func<T, object>>(expressionsToCheck[i], listExpression.Parameters);
var value = lambda.Compile().Invoke(entityDb);
if (value == null)
return true;
}