В 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.
Очевидно, вы ищете 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);
}
}
Это невозможно. ObjectA.PropertyA.PropertyB потерпит неудачу, если ObjectA равен null из-за разыменования null, что является ошибкой.
if(ObjectA != null && ObjectA.PropertyA ... работает из-за короткого замыкания, т.е. ObjectA.PropertyA никогда не будет проверен, если ObjectA равен null.
Первый предложенный вами способ является лучшим и наиболее ясным с точки зрения намерений. Если что, вы могли бы попробовать переделать дизайн без необходимости полагаться на такое количество нулей.
Вы можете сделать это:
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);
Этот код - "наименьшее количество кода", но не лучшая практика:
try
{
return ObjectA.PropertyA.PropertyB.PropertyC;
}
catch(NullReferenceException)
{
return null;
}
То, как вы это делаете, правильно.
Вы могли бы использовать трюк, подобный описанному здесь, используя выражения Linq:
int value = ObjectA.NullSafeEval(x => x.PropertyA.PropertyB.PropertyC, 0);
Но это намного медленнее, чем вручную проверять каждое свойство...
Я бы написал свой собственный метод в типе PropertyA (или метод расширения, если это не ваш тип), используя аналогичный паттерн для типа Nullable.
class PropertyAType
{
public PropertyBType PropertyB {get; set; }
public PropertyBType GetPropertyBOrDefault()
{
return PropertyB != null ? PropertyB : defaultValue;
}
}
Выполните рефакторинг для соблюдения Закона Деметры
Предполагая, что у вас есть пустые значения типов, один из подходов будет следующим:
var x = (((objectA ?? A.Empty).PropertyOfB ?? B.Empty).PropertyOfC ?? C.Empty).PropertyOfString;
Я большой поклонник C #, но очень хорошая вещь в новой Java (1.7?) - это.? оператор:
var x = objectA.?PropertyOfB.?PropertyOfC.?PropertyOfString;
Можете ли вы добавить метод в свой класс? Если нет, думали ли вы об использовании методов расширения? Вы можете создать метод расширения для вашего типа объекта под названием 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 или выше.