2015-09-03 2 views
1

В моем коде я имею доступа к значениям, призывающих несколько методов получения свойств:Аналоговый аналоговый оператор в .NET 3.5/4.0, VS 2012/2013?

IFoo1 a = objA.Prop1.Value; 
IFoo2 b = objB.Prop2.Prop3.Value; 
IFoo3 c = objC.Prop4.Prop5.Prop6.Value; 

Каждое свойство может быть пустым. Таким образом, чтобы получить доступ к каждому значение, я должен использовать вложенные если-блоки:

IFoo2 b = null; 

if(objB.Prop2!=null) 
{ 
    if(objB.Prop2.Prop3!=null) 
    { 
     b = objB.Prop2.Prop3.Value; 
    } 
} 

Как я могу улучшить этот код, чтобы уменьшить количество if-блоков? Могу ли я использовать любые лямбда-выражения, LINQ, IExpression и т. Д., Чтобы каким-то образом заменить его на:

IFoo2 b = GetVal (objB.Prop2.Prop3.Value);

Все PropX имеют разные типы, и у меня есть сотни таких свойств. Я должен использовать .NET 3.5 или хотя бы .NET 4.0. Я не могу использовать более поздние версии.

ВАЖНО EDIT:

Я должен также использовать Visual Studio 2012 и 2013. Я не могу предназначаться VS 2015.

+0

См. Здесь Студенческий класс: http://weblogs.asp.net/sreejukg/the-null-conditional-operator, у меня такая же проблема. –

+1

Какую версию VS вы используете? Если это VS2015, вы можете использовать оператор с нулевым условием C# 6, даже если таргетинг на .NET 3.5 или 4.0 –

ответ

1

Это будет делать это, но это очень и очень быстро и грязно. Некоторые очевидные недостатки:

  1. Для выражения A.B.C - мы вызываем «а», затем «a.b», затем «» A.B.C, проверка на нуль каждый раз. Мы должны сохранить возврат предыдущего вызова и изменить выражение члена для работы с нашим результатом. Только на самом деле проблема, если доступ к члену дорого, в противном случае это эквивалентно if (a != null && a.b != null && a.b.c != null) return a.b.c.d;, которая является довольно распространенным узором
  2. Он работает только для членов выражения

public static T GetOrNull<T>(Expression<Func<T>> expression) 
    where T : class 
{ 

    var memberExpressions = new List<MemberExpression>(); 
    var membExpress = expression.Body as MemberExpression; 
    while (membExpress != null) 
    { 
     memberExpressions.Add(membExpress); 
     membExpress = membExpress.Expression as MemberExpression; 
    } 
    memberExpressions.Skip(1).Reverse(); 

    foreach(var membExpr in memberExpressions.Skip(1).Reverse()) { 
     var lambdaExpr = Expression.Lambda(membExpr); 
     var currentRes = lambdaExpr.Compile().DynamicInvoke(); 
     if (currentRes == null) 
      return null; 
    } 

    return (T)Expression.Lambda(expression.Body).Compile().DynamicInvoke(); 
} 

И использовать его как это:

var tmp = new classA(); 

var res = GetOrNull(() => tmp.Prop1.Prop2); 
res.Dump(); //Gives null 

tmp.Prop1 = new classA.classB(); 
tmp.Prop1.Prop2 = new classA.classB.classC(); 
res = GetOrNull(() => tmp.Prop1.Prop2); 
res.Dump(); //returns object of type `classC` 
+2

Вы компилируете динамические методы во время выполнения, а затем вызываете их с отражением без какого-либо кэширования. Это хорошее решение, но его следует использовать очень осторожно. Эффективное воздействие будет в районе 10 000x медленнее или больше. – Jeff

-1

.NET 4.6 теперь есть?. оператор, который решает эту точную задачу.

http://www.volatileread.com/Wiki?id=2104

Это позволяет написать это.

IFoo3 c = objC? .Prop4? .Prop5? .Prop6? .Value;

Вы можете обойти это, создав вспомогательную функцию, но ясность и простота кода не приблизятся к этому. Если возможно, обновите эту функцию.

+0

Мне нужно использовать .NET 3.5 или .NET 4.0. –

+3

Новый оператор привязан к компилятору C#, а не к .NET. (Он в основном испускает все коды проверки ошибок, которые обычно делаются «вручную».) – user2864740

+2

Не имеет значения, какую версию .NET вы используете, если вы используете компилятор C# 6 –

0

Best вы можете, вероятно, использует соединение if состояние как

IFoo2 b = null; 

if(objB.Prop2 != null && objB.Prop2.Prop3 != null) 
{ 
    b = objB.Prop2.Prop3.Value; 
} 

(OR) с использованием Ternary Operator как

IFoo2 b = (objB.Prop2 != null && objB.Prop2.Prop3 != null) ? objB.Prop2.Prop3.Value : null; 
2

к сожалению, вам нужно C# 6 использовать нулевой условный оператор (?.).

Однако, вы можете имитировать его методы расширения, подобные этим:

static class Extensions 
{ 
    public static TReturn NCR<T, TReturn>(this T instance, Func<T, TReturn> getter) 
     where T : class 
     where TReturn : class 
    { 
     if (instance != null) 
      return getter(instance); 
     return null; 
    } 

    public static TReturn NCR<T, TReturn>(this T? instance, Func<T, TReturn> getter) 
     where T : struct 
     where TReturn : class 
    { 
     if (instance != null) 
      return getter(instance.Value); 
     return null; 
    } 

    public static TReturn? NCV<T, TReturn>(this T instance, Func<T, TReturn> getter) 
     where T : class 
     where TReturn : struct 
    { 
     if (instance != null) 
      return getter(instance); 
     return null; 
    } 

    public static TReturn? NCV<T, TReturn>(this T? instance, Func<T, TReturn> getter) 
    where T : struct 
    where TReturn : struct 
    { 
     if (instance != null) 
      return getter(instance.Value); 
     return null; 
    } 
} 

(NC означает Null-Условный, R обозначает ссылочного типа, V обозначает тип Value, это некрасиво, но, к сожалению C# Безразлично «т позволяют метод перегрузки, которые отличаются только родовыми связями)

Вы можете использовать их как это:

IFoo3 c = objC.NCR(_ => _.Prop4) 
       .NCR(_ => _.Prop5) 
       .NCR(_ => _.Prop6) 
       .NCR(_ => _.Value); 

(используйте NCV вместо NCR, если надлежащее чтобы получить возвращаемый тип значения)

Это все еще слишком многословно, но, по крайней мере, легче понять, что делает код.