1

在我的代码中,我必须访问调用多个属性获取器的值:

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

每个属性都可以为空。因此,要访问每个值,我必须使用嵌套的 if 块:

IFoo2 b = null;

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

如何改进此代码以减少 if 块的数量?我可以使用任何 lambda 表达式、LINQ、IExpression 等以某种方式将其替换为:

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

所有 PropX 都有不同的类型,我有数百个这样的属性。我必须使用 .NET 3.5 或至少 .NET 4.0。我不能使用任何更高版本。

重要编辑:

我还必须使用 Visual Studio 2012 和 2013。我不能针对 VS 2015。

4

4 回答 4

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-Conditional,R 代表引用类型,V 代表值类型;它很丑陋,但不幸的是 C# 不允许仅因泛型约束而不同的方法重载)

你可以像这样使用它们:

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

(如果要获取的属性返回值类型,则使用 NCV 而不是 NCR)

它仍然太冗长,但至少更容易看到代码在做什么。

于 2015-09-03T23:25:04.460 回答
1

这会做到这一点,但它非常非常快速和肮脏。一些明显的缺陷:

  1. 对于表达式 abc - 我们调用 'a',然后是 'ab',然后是 'abc',每次都检查 null。我们应该存储上一次调用的返回,并修改成员表达式以对我们的结果进行操作。只有当成员访问成本很高时才是真正的问题,否则它相当于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`
于 2015-09-03T23:20:00.960 回答
0

您可能做的最好的事情是使用复合if条件,例如

IFoo2 b = null;

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

(或)使用Ternary Operator类似

IFoo2 b = (objB.Prop2 != null && objB.Prop2.Prop3 != null) ? objB.Prop2.Prop3.Value : null;
于 2015-09-03T23:10:05.097 回答
-1

.NET 4.6 现在有一个 ?. 解决这个确切问题的运算符。

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

这使您可以像这样编写它。

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

你可以通过创建一个辅助函数来解决这个问题,但是代码的清晰性和简单性不会接近这个。如果可能,请升级以获得该功能。

于 2015-09-03T23:09:51.070 回答