C # элегантный способ проверить, если свойство свойства имеет значение null

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

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

РЕДАКТИРОВАТЬ Первоначально я сказал, что мой второй пример был как js, но я изменил его на псевдо-код, так как было правильно указано, что он не будет работать в js.

87
задан ROMANIA_engineer 19 September 2017 в 01:25
поделиться

9 ответов

Очевидно, вы ищете 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 , если какое-либо из свойств, допускающих значение NULL, нулевой; в противном случае значение Значение .

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);
    }
}
9
ответ дан 24 November 2019 в 07:44
поделиться

Это невозможно. ObjectA.PropertyA.PropertyB потерпит неудачу, если ObjectA равен null из-за разыменования null, что является ошибкой.

if(ObjectA != null && ObjectA.PropertyA ... работает из-за короткого замыкания, т.е. ObjectA.PropertyA никогда не будет проверен, если ObjectA равен null.

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

1
ответ дан 24 November 2019 в 07:44
поделиться

Вы можете сделать это:

class ObjectAType
{
    public int PropertyC
    {
        get
        {
            if (PropertyA == null)
                return 0;
            if (PropertyA.PropertyB == null)
                return 0;
            return PropertyA.PropertyB.PropertyC;
        }
    }
}



if (ObjectA != null)
{
    int value = ObjectA.PropertyC;
    ...
}

Или еще лучше:

private static int GetPropertyC(ObjectAType objectA)
{
    if (objectA == null)
        return 0;
    if (objectA.PropertyA == null)
        return 0;
    if (objectA.PropertyA.PropertyB == null)
        return 0;
    return objectA.PropertyA.PropertyB.PropertyC;
}


int value = GetPropertyC(ObjectA);
2
ответ дан 24 November 2019 в 07:44
поделиться

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

try
{
    return ObjectA.PropertyA.PropertyB.PropertyC;
}
catch(NullReferenceException)
{
     return null;
}
5
ответ дан 24 November 2019 в 07:44
поделиться

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

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

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

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

12
ответ дан 24 November 2019 в 07:44
поделиться

Я бы написал свой собственный метод в типе PropertyA (или метод расширения, если это не ваш тип), используя аналогичный паттерн для типа Nullable.

class PropertyAType
{
   public PropertyBType PropertyB {get; set; }

   public PropertyBType GetPropertyBOrDefault()
   {
       return PropertyB != null ? PropertyB : defaultValue;
   }
}
0
ответ дан 24 November 2019 в 07:44
поделиться

Выполните рефакторинг для соблюдения Закона Деметры

11
ответ дан 24 November 2019 в 07:44
поделиться

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

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

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

 var x = objectA.?PropertyOfB.?PropertyOfC.?PropertyOfString;
5
ответ дан 24 November 2019 в 07:44
поделиться

Можете ли вы добавить метод в свой класс? Если нет, думали ли вы об использовании методов расширения? Вы можете создать метод расширения для вашего типа объекта под названием 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 или выше.

16
ответ дан 24 November 2019 в 07:44
поделиться
Другие вопросы по тегам:

Похожие вопросы: