简体   繁体   English

在访问null对象的属性时,是否有一种方便的语法来返回null而不是异常

[英]Is there a handy syntax to return null rather than exception when accessing a property of a null object

Consider this simple c# example: 考虑一下这个简单的c#示例:

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

clearly this will throw a NullReferenceException because the MailingAddress proptery is null. 很明显,这将抛出NullReferenceException,因为MailingAddress proptery为null。

I could rewrite the second line as: 我可以将第二行重写为:

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

Is there a simpler way to say express this? 是否有更简单的方式来表达这一点?

This code is technically a violation of the Law of Demeter , so some would consider it bad form to write this in the first place. 这段代码在技术上违反了得墨忒耳法则 ,所以有些人会认为这首先写下来是不好的形式。

So no, there's no native syntax to accomplish what you want, but moving this code to a property in your Person class would make this calling code more clean, and also bring you in line with the law of demeter. 所以不,没有原生语法来完成你想要的东西,但是将这个代码移动到Person类中的属性会使这个调用代码更加干净,并且还使你符合demeter法则。

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

There's not really any good syntax for this. 对此没有任何好的语法。 The coalesce operator is part of it, but you need to handle traversing through a null, not just replacing a null. 聚结运营商是其中的一部分,但你需要处理透过一空穿越,而不仅仅是更换空。 One thing you could do would be to have a static "null object" for the class, something like: 你可以做的一件事就是为类创建一个静态的“null对象”,例如:

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

Then you could use the coalesce operator like so: 然后你可以像这样使用coalesce运算符:

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

If you want to go the extension method route, you could do something like this: 如果你想去扩展方法路线,你可以这样做:

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

Then you could do: 然后你可以这样做:

(person.MailingAddress.OrNew().Street)

You could use a device based on expression trees, so you'd write 可以使用基于表达式树的设备,所以你要写

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;
*/

Disclaimer : I haven't written this code, I just don't know where I originally found it either. 免责声明 :我没有写过这段代码,我只是不知道我最初在哪里找到它。 Anyone recognize this helper? 有人认出这个帮手吗?

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;
}

You can use this Extension method: 您可以使用此扩展方法:

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

Example: 例:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM