如何检查深度lambda表达式中的空值?

Joh*_*wis 14 c# linq lambda

如何在深层lamda表达式中检查空值?

比方说,我有一个嵌套了几层深度的类结构,我想执行以下lambda:

x => x.Two.Three.Four.Foo
Run Code Online (Sandbox Code Playgroud)

如果Two,Three或Four为null,我希望它返回null,而不是抛出System.NullReferenceException.

public class Tests
{
    // This test will succeed
    [Fact]
    public void ReturnsValueWhenClass2NotNull()
    {
        var one = new One();
        one.Two = new Two();
        one.Two.Three = new Three();
        one.Two.Three.Four = new Four();
        one.Two.Three.Four.Foo = "blah";

        var result = GetValue(one, x => x.Two.Three.Four.Foo);

        Assert.Equal("blah", result);
    }

    // This test will fail
    [Fact]
    public void ReturnsNullWhenClass2IsNull()
    {
        var one = new One();

        var result = GetValue(one, x => x.Two.Three.Four.Foo);

        Assert.Equal(null, result);
    }

    private TResult GetValue<TModel, TResult>(TModel model, Expression<Func<TModel, TResult>> expression)
    {
        var func = expression.Compile();
        var value = func(model);
        return value;
    }

    public class One
    {
        public Two Two { get; set; }
    }

    public class Two
    {
        public Three Three { get; set; }
    }

    public class Three
    {
        public Four Four { get; set; }
    }

    public class Four
    {
        public string Foo { get; set; }
        public string Bar { get; set; }
    }
}
Run Code Online (Sandbox Code Playgroud)

更新:

一种解决方案是捕获NullReferenceException,如下所示:

    private TResult GetValue<TModel, TResult>(TModel model, Expression<Func<TModel, TResult>> expression)
    {
        TResult value;
        try
        {
            var func = expression.Compile();
            value = func(model);
        }
        catch (NullReferenceException)
        {
            value = default(TResult);
        }
        return value;
    }
Run Code Online (Sandbox Code Playgroud)

但是我不愿意承担捕捉异常的费用,在我看来,异常并不是特别的.我希望在我的域名中经常出现这种情况.

更新2:

另一个解决方案是修改属性getter,如下所示:

    public class One
    {
        private Two two;
        public Two Two
        {
            get
            {
                return two ?? new Two();
            }
            set
            {
                two = value;
            }
        }
    }
Run Code Online (Sandbox Code Playgroud)

哪个对我的域名来说是好的,但有时候我真的期望一个属性返回null.我检查了Josh E的答案是有帮助的,因为它在某些情况下非常接近我需要的.

Luc*_*ero 17

您可以使用通用帮助程序扩展方法执行此操作,例如:

public static class Get {
    public static T IfNotNull<T, U>(this U item, Func<U, T> lambda) where U: class {
        if (item == null) {
            return default(T);
        }
        return lambda(item);
    }
}

var one = new One();
string fooIfNotNull = one.IfNotNull(x => x.Two).IfNotNull(x => x.Three).IfNotNull(x => x.Four).IfNotNull(x => x.Foo);
Run Code Online (Sandbox Code Playgroud)


Gab*_*art 13

你不能以简洁的方式做到这一点.您可以使lambda多行,或使用嵌套的三元运算符:

var result = GetValue(one, x => x.Two == null ? null :
                                x.Two.Three == null ? null :
                                x.Two.Three.Four == null ? null :
                                x.Two.Three.Four.Foo;
Run Code Online (Sandbox Code Playgroud)

丑陋,我知道.

  • 这是更简洁的一个.也许(x => x.Two.Three.Four.Foo); 见http://maybe.codeplex.com/ (2认同)

Eri*_*ert 7

简洁地这样做需要一个尚未实现的操作员.我们考虑过添加一个运算符".?" 到C#4.0会有你想要的语义,但不幸的是它不符合我们的预算.我们会将其视为该语言的假设未来版本.