是否有方便的语法在访问空对象的属性时返回空而不是异常

本文关键字:返回 异常 属性 对象 语法 访问 是否 方便 | 更新日期: 2023-09-27 18:15:39

考虑这个简单的c#示例:

var person = new Person {Name = "Fred", MailingAddress=null };
var result = String.Format("{0} lives at {1}",person.Name, person.MailingAddress.Street);

显然这将抛出NullReferenceException,因为MailingAddress属性是空的。

我可以将第二行重写为:

var result = String.Format("{0} lives at {1}", person.Name, person.MailingAddress == null ? (String)null : person.MailingAddress.Street);

有更简单的表达方式吗?

是否有方便的语法在访问空对象的属性时返回空而不是异常

这段代码在技术上违反了得墨忒耳定律,所以有些人会认为首先写这段代码是不好的形式。

所以,不,没有原生语法来完成你想要的,但是将这些代码移动到你的Person类的属性中会使调用代码更干净,也使你符合得米忒律。

public string StreetAddress{
   get { return this.MailingAddress == null ? 
                   (String)null : person.MailingAddress.Street; }
}

没有合适的语法。合并运算符是其中的一部分,但是您需要处理在中遍历 null,而不仅仅是替换null。您可以做的一件事是为类设置一个静态的"null对象",类似于:

public class Address 
{
  public static Address Null = new Address();
  // Rest of the class goes here
}

那么你可以像这样使用合并运算符:

(person.MailingAddress ?? Address.Null).Street

如果你想使用扩展方法,你可以这样做:

public static class NullExtension
{
    public static T OrNew<T>(this T thing)
        where T: class, new()
    {
        return thing ?? new T();
    }
}

那么你可以这样做:

(person.MailingAddress.OrNew().Street)

可以使用基于表达式树的设备,因此您可以写

var n = person.NullPropagate(p => p.Contact.MailingAddress.StreetAddress.Number);
/* having the effect of:
   (person == null)
   ? defaultValue
   : (person.Contact == null)
     ? defaultValue
     : (person.Contact.MailingAddress == null)
       ? defaultValue
       : (person.Contact.MailingAddress.StreetAddress == null)
         ? defaultValue
         : person.Contact.MailingAddress.StreetAddress.Number;
*/

免责声明:我没有写这段代码,我只是不知道我最初在哪里找到它。有人认识这个帮手吗?

public static R NullPropagate<T, R>(this T source, Expression<Func<T, R>> expression, R defaultValue)
{
    var safeExp = Expression.Lambda<Func<T, R>>(
        WrapNullSafe(expression.Body, Expression.Constant(defaultValue)),
        expression.Parameters[0]);
    var safeDelegate = safeExp.Compile();
    return safeDelegate(source);
}
private static Expression WrapNullSafe(Expression expr, Expression defaultValue)
{
    Expression obj;
    Expression safe = expr;
    while (!IsNullSafe(expr, out obj))
    {
        var isNull = Expression.Equal(obj, Expression.Constant(null));
        safe = Expression.Condition (isNull, defaultValue, safe);
        expr = obj;
    }
    return safe;
}
private static bool IsNullSafe(Expression expr, out Expression nullableObject)
{
    nullableObject = null;
    if (expr is MemberExpression || expr is MethodCallExpression)
    {
        Expression obj;
        MemberExpression memberExpr = expr as MemberExpression;
        MethodCallExpression callExpr = expr as MethodCallExpression;
        if (memberExpr != null)
        {
            // Static fields don't require an instance
            FieldInfo field = memberExpr.Member as FieldInfo;
            if (field != null && field.IsStatic)
                return true;
            // Static properties don't require an instance
            PropertyInfo property = memberExpr.Member as PropertyInfo;
            if (property != null)
            {
                MethodInfo getter = property.GetGetMethod();
                if (getter != null && getter.IsStatic)
                    return true;
            }
            obj = memberExpr.Expression;
        }
        else
        {
            // Static methods don't require an instance
            if (callExpr.Method.IsStatic)
                return true;
            obj = callExpr.Object;
        }
        // Value types can't be null
        if (obj.Type.IsValueType)
            return true;
        // Instance member access or instance method call is not safe
        nullableObject = obj;
        return false;
    }
    return true;
}

您可以使用这个扩展方法:

public static TResult Maybe<TInput, TResult>(this TInput value, Func<TInput, TResult> evaluator, TResult failureValue)
            where TInput : class
        {
            return (value != null) ? evaluator(value) : failureValue;
        }

的例子:

person.MailingAddress.MayBe(p=>p.Street,default(Street))