如何为运行时生成的Getter添加空安全解引用,返回null而非抛出异常
运行时生成带空安全的属性Getter方法
我需要在运行时生成一个程序集,存储约1000种类型所有属性的Getter方法。当前实现的CreateGetter方法生成的代码没有空安全性,当链式调用中某个环节返回null时会抛出异常,希望生成的代码能支持空传播(类似?.)或者通过临时变量加判断实现空安全。
当前实现代码
public static void CreateGetter(PropertyInfo propertyInfo) { if (propertyInfo.DeclaringType == null || propertyInfo.GetIndexParameters().Length > 0) { propertyGetters.Add(propertyInfo, null); return; } Func<object, object> compiledGetterLambda = null; try { // Instance parameter, passed as input as object ParameterExpression objectParameterExpression = Expression.Parameter(typeof(object), "instance"); // Cast instance to the actual type UnaryExpression instanceExpression = Expression.TypeAs(objectParameterExpression, propertyInfo.DeclaringType); // Call the getter and retrieve the value MemberExpression propertyValueExpression = Expression.Property(instanceExpression, propertyInfo); if (propertyInfo.PropertyType == typeof(SomeClass)) { instanceExpression = Expression.TypeAs(propertyValueExpression, typeof(SomeClass)); propertyValueExpression = Expression.Property(instanceExpression, nameof(SomeClass.Id)); } // Convert the value to object UnaryExpression propertyValueObjectExpression = Expression.Convert(propertyValueExpression, typeof(object)); // Create lambda Expression<Func<object, object>> getterLambda = Expression.Lambda<Func<object, object>>(propertyValueObjectExpression, objectParameterExpression); rawPropertyGetters.Add(propertyInfo, getterLambda); // Compile lambda compiledGetterLambda = getterLambda.Compile(); } catch { // ignored } propertyGetters.Add(propertyInfo, compiledGetterLambda); }
当前生成的无空安全代码示例
public static object getValue_SomeClass(object instance) { return ((instance as BaseClass).get_SomeClass() as SomeClass).get_Id(); }
期望的空安全代码示例
public static object getValue_SomeClass(object instance) { return ((instance as BaseClass)?.get_SomeClass() as SomeClass)?.get_Id() ?? null; }
解决方案
方案一:临时变量+条件判断实现空安全
这种方式逻辑直观,通过声明临时变量存储每一步的结果,逐个检查是否为null,若为空则直接返回null,否则继续执行后续逻辑。
修改后的CreateGetter方法:
public static void CreateGetter(PropertyInfo propertyInfo) { if (propertyInfo.DeclaringType == null || propertyInfo.GetIndexParameters().Length > 0) { propertyGetters.Add(propertyInfo, null); return; } Func<object, object> compiledGetterLambda = null; try { ParameterExpression objectParameter = Expression.Parameter(typeof(object), "instance"); List<ParameterExpression> tempVariables = new List<ParameterExpression>(); // 第一步:转换实例到声明类型并检查null var declaredTypeTemp = Expression.Variable(propertyInfo.DeclaringType, "declaredInstance"); tempVariables.Add(declaredTypeTemp); var assignDeclaredInstance = Expression.Assign(declaredTypeTemp, Expression.TypeAs(objectParameter, propertyInfo.DeclaringType)); var checkDeclaredInstanceNull = Expression.Condition( Expression.Equal(declaredTypeTemp, Expression.Constant(null, propertyInfo.DeclaringType)), Expression.Constant(null, typeof(object)), Expression.Empty() ); // 第二步:获取目标属性并检查null var propertyValueTemp = Expression.Variable(propertyInfo.PropertyType, "propertyValue"); tempVariables.Add(propertyValueTemp); var assignPropertyValue = Expression.Assign(propertyValueTemp, Expression.Property(declaredTypeTemp, propertyInfo)); var checkPropertyValueNull = Expression.Condition( Expression.Equal(propertyValueTemp, Expression.Constant(null, propertyInfo.PropertyType)), Expression.Constant(null, typeof(object)), Expression.Empty() ); Expression finalExpression; // 处理SomeClass的特殊逻辑:获取Id属性 if (propertyInfo.PropertyType == typeof(SomeClass)) { var someClassTemp = Expression.Variable(typeof(SomeClass), "someClassInstance"); tempVariables.Add(someClassTemp); var assignSomeClass = Expression.Assign(someClassTemp, Expression.TypeAs(propertyValueTemp, typeof(SomeClass))); var checkSomeClassNull = Expression.Condition( Expression.Equal(someClassTemp, Expression.Constant(null, typeof(SomeClass))), Expression.Constant(null, typeof(object)), Expression.Empty() ); finalExpression = Expression.Block( assignSomeClass, checkSomeClassNull, Expression.Convert(Expression.Property(someClassTemp, nameof(SomeClass.Id)), typeof(object)) ); } else { finalExpression = Expression.Convert(propertyValueTemp, typeof(object)); } // 组合所有逻辑块 var blockExpression = Expression.Block( tempVariables, assignDeclaredInstance, checkDeclaredInstanceNull, assignPropertyValue, checkPropertyValueNull, finalExpression ); var getterLambda = Expression.Lambda<Func<object, object>>(blockExpression, objectParameter); rawPropertyGetters.Add(propertyInfo, getterLambda); compiledGetterLambda = getterLambda.Compile(); } catch { // ignored } propertyGetters.Add(propertyInfo, compiledGetterLambda); }
生成的代码示例:
public static object getValue_SomeClass(object instance) { BaseClass declaredInstance = instance as BaseClass; if (declaredInstance == null) return null; SomeClass propertyValue = declaredInstance.get_SomeClass(); if (propertyValue == null) return null; SomeClass someClassInstance = propertyValue as SomeClass; if (someClassInstance == null) return null; return (object)someClassInstance.get_Id(); }
方案二:嵌套条件表达式模拟空传播
通过嵌套Expression.Condition模拟?.的空传播逻辑,无需显式声明临时变量,代码结构更简洁,编译后的IL逻辑与临时变量方式一致。
修改后的CreateGetter方法:
public static void CreateGetter(PropertyInfo propertyInfo) { if (propertyInfo.DeclaringType == null || propertyInfo.GetIndexParameters().Length > 0) { propertyGetters.Add(propertyInfo, null); return; } Func<object, object> compiledGetterLambda = null; try { ParameterExpression objectParameter = Expression.Parameter(typeof(object), "instance"); Expression currentExpression = Expression.TypeAs(objectParameter, propertyInfo.DeclaringType); // 检查转换后的实例是否为null,为空则返回null currentExpression = Expression.Condition( Expression.Equal(currentExpression, Expression.Constant(null, propertyInfo.DeclaringType)), Expression.Constant(null, typeof(object)), Expression.Property(currentExpression, propertyInfo) ); // 处理SomeClass的特殊逻辑 if (propertyInfo.PropertyType == typeof(SomeClass)) { var someClassExpr = Expression.TypeAs(currentExpression, typeof(SomeClass)); // 检查转换后的SomeClass实例是否为null currentExpression = Expression.Condition( Expression.Equal(someClassExpr, Expression.Constant(null, typeof(SomeClass))), Expression.Constant(null, typeof(object)), Expression.Property(someClassExpr, nameof(SomeClass.Id)) ); } // 最终转换为object类型 currentExpression = Expression.Convert(currentExpression, typeof(object)); var getterLambda = Expression.Lambda<Func<object, object>>(currentExpression, objectParameter); rawPropertyGetters.Add(propertyInfo, getterLambda); compiledGetterLambda = getterLambda.Compile(); } catch { // ignored } propertyGetters.Add(propertyInfo, compiledGetterLambda); }
生成的逻辑等价于:
public static object getValue_SomeClass(object instance) { var temp = instance as BaseClass; if (temp == null) return null; var temp2 = temp.get_SomeClass(); if (temp2 == null) return null; var temp3 = temp2 as SomeClass; if (temp3 == null) return null; return (object)temp3.get_Id(); }
注意事项
- 两种方案均能避免空引用异常,临时变量方式更易调试和扩展,嵌套条件表达式方式代码更简洁。
- 若涉及值类型属性,需额外判断属性类型是否为可空值类型或引用类型,避免不必要的null检查。
- 建议保留异常捕获逻辑的同时添加日志记录,便于排查潜在问题。
内容的提问来源于stack exchange,提问作者CaptTaifun
相关产品推荐
相关产品推荐

