You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为运行时生成的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 03:21:30