Возможные ловушки использования этого (основанного на методе расширения) сокращения
C#6 Update
ВС # 6?.
is now a language feature:
// C#1-5
propertyValue1 = myObject != null ? myObject.StringProperty : null;
// C#6
propertyValue1 = myObject?.StringProperty;
Вопрос ниже по-прежнему относится к более старым версиям, но при разработке нового приложения с использованием нового?.
Оператор гораздо лучшая практика.
Original Question:
Я регулярно хочу получить доступ к свойствам на возможно нулевых объектах:
string propertyValue1 = null;
if( myObject1 != null )
propertyValue1 = myObject1.StringProperty;
int propertyValue2 = 0;
if( myObject2 != null )
propertyValue2 = myObject2.IntProperty;
И так далее...
Я использую это так часто, что у меня есть фрагмент для этого.
Вы можете сократить это до некоторой степени встроенным, если:
propertyValue1 = myObject != null ? myObject.StringProperty : null;
Однако это немного неуклюже, особенно если задано много свойств или если несколько уровней могут быть нулевыми, например:
propertyValue1 = myObject != null ?
(myObject.ObjectProp != null ? myObject.ObjectProp.StringProperty) : null : null;
Что я действительно хочу, так это??
синтаксис стиля, который отлично работает для напрямую нулевых типов:
int? i = SomeFunctionWhichMightReturnNull();
propertyValue2 = i ?? 0;
Итак, я придумал следующее:
public static TResult IfNotNull<T, TResult>( this T input, Func<T, TResult> action, TResult valueIfNull )
where T : class
{
if ( input != null ) return action( input );
else return valueIfNull;
}
//lets us have a null default if the type is nullable
public static TResult IfNotNull<T, TResult>( this T input, Func<T, TResult> action )
where T : class
where TResult : class
{ return input.IfNotNull( action, null ); }
Это позволяет мне использовать этот синтаксис:
propertyValue1 = myObject1.IfNotNull( x => x.StringProperty );
propertyValue2 = myObject2.IfNotNull( x => x.IntProperty, 0);
//or one with multiple levels
propertyValue1 = myObject.IfNotNull(
o => o.ObjectProp.IfNotNull( p => p.StringProperty ) );
Это упрощает эти вызовы, но я не уверен в том, чтобы включить этот метод расширения - это делает код немного легче для чтения, но за счет расширения объекта. Это появилось бы на всем, хотя я мог бы поместить это в специально упомянутое пространство имен.
Этот пример довольно простой, чуть более сложный, если сравнивать два свойства объекта, допускающие обнуляемость:
if( ( obj1 == null && obj2 == null ) ||
( obj1 != null && obj2 != null && obj1.Property == obj2.Property ) )
...
//becomes
if( obj1.NullCompare( obj2, (x,y) => x.Property == y.Property )
...
Каковы недостатки использования расширений таким образом? Другие кодеры могут быть сбиты с толку? Это просто злоупотребление расширениями?
Я думаю, что я действительно хочу здесь, это расширение компилятора / языка:
propertyValue1 = myObject != null ? myObject.StringProperty : null;
//becomes
propertyValue1 = myObject?StringProperty;
Это значительно усложнит сложный случай:
propertyValue1 = myObject != null ?
(myObject.ObjectProp != null ? myObject.ObjectProp.StringProperty) : null
//becomes
propertyValue1 = myObject?ObjectProp?StringProperty;
Это будет работать только для типов значений, но вы можете вернуть обнуляемые эквиваленты:
int? propertyValue2 = myObject?ObjectProp?IntProperty;
//or
int propertyValue3 = myObject?ObjectProp?IntProperty ?? 0;