C # элегантный способ проверить, является ли свойство свойства нулевым - PullRequest
81 голосов
/ 12 августа 2010

В C # скажем, что вы хотите извлечь значение из PropertyC в этом примере, и ObjectA, PropertyA и PropertyB могут быть нулевыми.

ObjectA.PropertyA.PropertyB.PropertyC

Как я могу безопасно получить PropertyC с наименьшим количеством кода?

Прямо сейчас я бы проверил:

if(ObjectA != null && ObjectA.PropertyA !=null && ObjectA.PropertyA.PropertyB != null)
{
    // safely pull off the value
    int value = objectA.PropertyA.PropertyB.PropertyC;
}

Было бы неплохо сделать что-то более подобное (псевдокод).

int value = ObjectA.PropertyA.PropertyB ? ObjectA.PropertyA.PropertyB : defaultVal;

Возможно, еще больше рухнул оператор с нулевым слиянием.

EDIT Первоначально я сказал, что мой второй пример похож на js, но я изменил его на psuedo-код, поскольку было правильно указано, что он не будет работать в JS.

Ответы [ 21 ]

85 голосов
/ 07 ноября 2014

В C # 6 вы можете использовать нулевой условный оператор. Итак, оригинальный тест будет:

 int? value = objectA?.PropertyA?.PropertyB?.PropertyC;
26 голосов
/ 02 апреля 2014

Метод короткого продления:

public static TResult IfNotNull<TInput, TResult>(this TInput o, Func<TInput, TResult> evaluator)
  where TResult : class where TInput : class
{
  if (o == null) return null;
  return evaluator(o);
}

Использование

PropertyC value = ObjectA.IfNotNull(x => x.PropertyA).IfNotNull(x => x.PropertyB).IfNotNull(x => x.PropertyC);

Этот простой метод расширения и многое другое вы можете найти на http://devtalk.net/csharp/chained-null-checks-and-the-maybe-monad/

EDIT:

После использования этого момента я думаю, что правильное имя для этого метода должно быть IfNotNull () вместо оригинального With ().

16 голосов
/ 12 августа 2010

Можете ли вы добавить метод в ваш класс?Если нет, задумывались ли вы об использовании методов расширения?Вы можете создать метод расширения для вашего типа объекта с именем GetPropC().

Пример:

public static class MyExtensions
{
    public static int GetPropC(this MyObjectType obj, int defaltValue)
    {
        if (obj != null && obj.PropertyA != null & obj.PropertyA.PropertyB != null)
            return obj.PropertyA.PropertyB.PropertyC;
        return defaltValue;
    }
}

Использование:

int val = ObjectA.GetPropC(0); // will return PropC value, or 0 (defaltValue)

Кстати, это предполагает васиспользуете .NET 3 или выше.

12 голосов
/ 12 августа 2010

То, как вы делаете это правильно.

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

int value = ObjectA.NullSafeEval(x => x.PropertyA.PropertyB.PropertyC, 0);

Но гораздо медленнее, чем вручную проверять каждое свойство ...

11 голосов
/ 12 августа 2010

Фактор для соблюдения Закона Деметры

10 голосов
/ 20 мая 2014

Обновление 2014: в C # 6 появился новый оператор ?., который называется «безопасная навигация» или «распространение нуль»

parent?.child

Подробнее http://blogs.msdn.com/b/jerrynixon/archive/2014/02/26/at-last-c-is-getting-sometimes-called-the-safe-navigation-operator.aspx

Это давно популярный запрос https://visualstudio.uservoice.com/forums/121579-visual-studio/suggestions/3990187-add-operator-to-c-?tracking_code=594c10a522f8e9bc987ee4a5e2c0b38d

9 голосов
/ 12 августа 2010

Вы явно ищете Nullable Monad :

string result = new A().PropertyB.PropertyC.Value;

становится

string result = from a in new A()
                from b in a.PropertyB
                from c in b.PropertyC
                select c.Value;

Возвращает null, если какое-либо из значений Nullable имеет значение null; в противном случае значение Value.

class A { public B PropertyB { get; set; } }
class B { public C PropertyC { get; set; } }
class C { public string Value { get; set; } }

Методы расширения LINQ:

public static class NullableExtensions
{
    public static TResult SelectMany<TOuter, TInner, TResult>(
        this TOuter source,
        Func<TOuter, TInner> innerSelector,
        Func<TOuter, TInner, TResult> resultSelector)
        where TOuter : class
        where TInner : class
        where TResult : class
    {
        if (source == null) return null;
        TInner inner = innerSelector(source);
        if (inner == null) return null;
        return resultSelector(source, inner);
    }
}
5 голосов
/ 12 августа 2010

Предполагая, что у вас есть пустые значения типов, один из подходов будет следующим:

var x = (((objectA ?? A.Empty).PropertyOfB ?? B.Empty).PropertyOfC ?? C.Empty).PropertyOfString;

Я большой поклонник C #, но очень приятная вещь в новой Java (1.7?) Это.? Оператор:

 var x = objectA.?PropertyOfB.?PropertyOfC.?PropertyOfString;
5 голосов
/ 12 августа 2010

Этот код является «наименьшим количеством кода», но не лучшей практикой:

try
{
    return ObjectA.PropertyA.PropertyB.PropertyC;
}
catch(NullReferenceException)
{
     return null;
}
4 голосов
/ 09 октября 2010

Проверьте это сообщение в блоге out.Я думаю, что это очень элегантный метод для цепных нулевых проверок.Есть много подобных реализаций этого, но мне нравится эта, потому что она перестает оценивать, как только в цепочке обнаруживается нуль.

Весь исходный код находится на github .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...