如何从作为参数的Linq表达式获取值并避免空引用异常?
解决Linq Expression表达式链中空引用异常的问题
当你传递包含多级成员访问的Linq Expression到GetVal方法时,若链中某个对象为null,执行编译后的委托就会抛出NullReferenceException。以下两种方案可以解决这个问题:
方案一:调用时使用空传播操作符(?.)
直接在表达式中使用C#的空传播操作符,让表达式本身处理空值情况。修改调用代码即可:
private static void TestGetVal() { MyEntity p = new MyEntity(); // 使用?.自动处理MySubEntity为null的情况,此时返回null而非抛出异常 var myvalue = GetVal(p, p => p.MySubEntity?.Description); }
这种方式简单直接,不需要修改GetVal方法,由调用者明确控制空值处理逻辑。如果MySubEntity为null,表达式会返回null,不会触发异常。
方案二:在GetVal方法中自动修改表达式树添加空检测
如果你希望对所有传入的表达式自动处理空值,无需调用者手动添加?.,可以通过解析并修改表达式树,在每个成员访问节点前插入空值检查逻辑。
实现代码如下:
public static object GetVal<TModel, TResult>(TModel model, Expression<Func<TModel, TResult>> expression) { // 修改表达式树,添加空值检测逻辑 var safeExpression = AddNullChecks(expression.Body, expression.Parameters[0]); var safeLambda = Expression.Lambda<Func<TModel, TResult>>(safeExpression, expression.Parameters); Func<TModel, TResult> compiledDelegate = safeLambda.Compile(); return compiledDelegate(model); } private static Expression AddNullChecks(Expression expression, ParameterExpression parameter) { if (expression is MemberExpression memberExpr) { // 递归处理成员访问的源表达式 var sourceWithNullCheck = AddNullChecks(memberExpr.Expression, parameter); // 创建空检查:如果源为null,返回默认值;否则访问成员 var nullCheck = Expression.Condition( Expression.Equal(sourceWithNullCheck, Expression.Constant(null)), Expression.Default(memberExpr.Type), memberExpr ); return nullCheck; } // 如果是参数表达式,直接返回 else if (expression == parameter) { return expression; } // 其他类型表达式(如常量、方法调用等)直接返回 else { return expression; } }
这个方法会递归遍历表达式树中的所有成员访问节点,自动为每个成员访问添加空值判断:如果成员的源对象为null,则返回该成员类型的默认值(比如string类型返回null,值类型返回默认值如0),否则正常访问成员。
两种方案对比
- 方案一:代码量少,灵活可控,适合需要针对特定表达式定制空值处理的场景。
- 方案二:对调用者透明,统一处理所有表达式的空值情况,适合通用工具类场景,但表达式树解析逻辑相对复杂,需要考虑更多表达式类型的兼容(比如方法调用、索引器等)。
内容的提问来源于stack exchange,提问作者mattpm
相关产品推荐
相关产品推荐

