Есть ли удобный синтаксис для возврата значения null, а не исключения при доступе к свойству нулевого объекта - PullRequest
4 голосов
/ 25 ноября 2011

Рассмотрим простой пример c #:

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

ясно, что это вызовет исключение NullReferenceException, так как свойство MailingAddress имеет значение null.

Я мог бы переписать вторую строку как:

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

Есть ли более простой способ выразить это?

Ответы [ 4 ]

4 голосов
/ 25 ноября 2011

Этот код технически является нарушением Закона Деметры , поэтому некоторые сочли бы это плохим тоном писать это в первую очередь.

Так что нет, нет собственного синтаксиса длядостигните желаемого, но перемещение этого кода в свойство в вашем классе Person сделает этот вызывающий код более чистым, а также приведет вас в соответствие с законом деметры.

public string StreetAddress{
   get { return this.MailingAddress == null ? 
                   (String)null : person.MailingAddress.Street; }
}
3 голосов
/ 25 ноября 2011

Нет хорошего синтаксиса для этого. Оператор coalesce является его частью, но вам нужно обрабатывать обход от до null, а не просто заменять null. Одна вещь, которую вы могли бы сделать, это иметь статический «нулевой объект» для класса, что-то вроде:

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

Тогда вы можете использовать оператор coalesce следующим образом:

(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)
1 голос
/ 25 ноября 2011

Вы могли бы использовать устройство, основанное на деревьях выражений, поэтому вы бы написали

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;
}
0 голосов
/ 25 ноября 2011

Вы можете использовать этот метод расширения:

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))
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...