C# 如何检查深lambda表达式中的空值?
如何检查深lamda表达式中的空值 例如,我有一个嵌套了几层的类结构,我想执行以下lambda:C# 如何检查深lambda表达式中的空值?,c#,linq,lambda,C#,Linq,Lambda,如何检查深lamda表达式中的空值 例如,我有一个嵌套了几层的类结构,我想执行以下lambda: x => x.Two.Three.Four.Foo 如果两个、三个或四个为null,我希望它返回null,而不是抛出System.NullReferenceException public class Tests { // This test will succeed [Fact] public void ReturnsValueWhenClass2NotNull()
x => x.Two.Three.Four.Foo
如果两个、三个或四个为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; }
}
}
这对于我的域来说基本上是可以的,但有时我真的希望属性返回null。我检查了Josh E的答案,认为它很有用,因为它在某些情况下非常接近我需要的答案。您可以使用一个通用的助手扩展方法来实现这一点,例如:
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);
private Two _two;
public Two Two
{
get
{
if (null == _two)
return new Two();
else
return _two;
}
}
公共静态类Get{
公共静态T IfNotNull(此U项,Func lambda),其中U:class{
如果(项==null){
返回默认值(T);
}
返回lambda(项目);
}
}
var one=新的一个();
字符串fooinfotnull=one.IfNotNull(x=>x.Two).IfNotNull(x=>x.Three.IfNotNull(x=>x.Four.IfNotNull)(x=>x.Foo);
在使用属性之前,请始终初始化属性。向类1、2、3和4添加构造函数。在构造函数中,初始化属性,使其不为空。您不能以简洁的方式完成此操作。可以将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;
丑陋,我知道。您可以修改您的getter,使其内容如下:
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);
private Two _two;
public Two Two
{
get
{
if (null == _two)
return new Two();
else
return _two;
}
}
我对c#不太熟悉,但也许有某种方法可以从ruby中实现“AND”模式,从而在不影响实现的情况下解决这个问题
这个概念在Haskell中也被称为Maybe Monad
这篇文章的标题似乎很有希望。我发现coalesce操作符有时对此很有用。不过,这只有在存在可插入的对象的默认/空等效版本时才有帮助 例如,有时当我打开XML时
IEnumeratable<XElement> sample;
sample.Where(S => (S.Attribute["name"] ?? new XAttribute("name","")).Value.StartsWith("Hello"))...
i可计量样本;
sample.Where(S=>(S.Attribute[“name”]?new XAttribute(“name”,”).Value.StartsWith(“Hello”)。。。
根据默认对象的检索方式,这可能会很冗长,上面的示例并没有多大用处,但您已经明白了这一点。对于读取XML属性的特殊情况,我有一个返回属性值或空字符串的扩展方法。简单地执行此操作需要一个尚未实现的运算符。我们考虑在C#4.0中添加一个操作符“?”,它将具有您想要的语义,但不幸的是,它不适合我们的预算。我们将把它当作一个假设的未来版本的语言。 现在可以使用CODULTEX项目。 语法是:
string result = One.Maybe(o => o.Two.Three.Four.Foo);
string cityName = Employee.Maybe(e => e.Person.Address.CityName);
我将一个函数转换为.IFNotNull方法,该函数使用了大量if语句来避免空值,该方法用于我从深度为4和5级的XSD转换的类 以下是几行转换后的代码:
ProdYear.Previous_CUMULATIVE_CARBON_VALUE=year.IfNotNull(x=>x.Previous_CUMULATIVE).IfNotNull(y=>y.CARBON_CO2).IfNotNull(z=>z.VALUE).ToDouble();
ProdYear.Previor\u CUMULATIVE\u CARBON\u CO2\u UOM=year.IfNotNull(x=>x.Previor\u CUMULATIVE)。IfNotNull(y=>y.CARBON\u CO2)。IfNotNull(z=>z.UOM);
以下是一些有趣的统计数据:
1) 这个新方法比使用If语句运行变量的时间长3.7409倍。2) 我将功能行计数从157减少到59。
3) DevXPress的“维护复杂性”得分为。当我转换到Lambda语句时,它从984增加到2076,这在理论上更难维护 我已经编写了一个扩展方法,使您能够执行以下操作:
blah.GetValueOrDefault(x => x.Two.Three.Four.Foo);
在返回表达式值之前,它使用表达式树在每个节点上构建一个嵌套的空值条件检查;创建的表达式树被编译成一个Func
并被缓存,因此相同调用的后续使用应该以几乎本机的速度运行
如果愿意,还可以传入要返回的默认值:
blah.GetValueOrDefault(x => x.Two.Three.Four.Foo, Foo.Empty);
我写了一篇关于它的博客。在这种情况下,我想返回Foo或Bar类型的默认值。我真正想要避免的是,如果表达式树中更高的部分为空,则会出现异常。我编辑了答案,并添加了一个代码示例,该示例编译得很好,应该可以做到这一点。这容易吗?那么优雅+1,并且没有因反射而影响运行时性能。有没有人根据Gabe的解决方案或“正常”方法对此进行基准测试?我通常会这样做,但在这个域中,属性有时会设置为null。这是一种有效的行为。有趣的是,这篇文章几乎与我通过思考得出的解决方案完全相同,请参阅我的帖子……哇,完全错过了它,我想我是在寻找“可能”这个词可以。修改实现以保存客户端代码中的一些行会触发各种警报。我倾向于不同意这是一个问题:我将此称为防御编码。上面的代码确保了一个属性的值在没有与该属性/对象的任何使用者共享该知识的情况下永远不会为null。如果我在_Two为null时继续调用Two,我会不断得到Two的新实例。。。ewgood point。在这种情况下,您可以修改它,在返回它之前将_two设置为一个新的two()实例,例如if(null==_two)_two=new two();返回2;这太好了!Delphi prism对它的“:”操作符做了同样的操作:我支持它。这将使许多代码变得更加干净!此功能现在已在c#6中!我尝试在这个问题案例中使用“?”,但这是不允许的。。。它导致错误
错误CS8072表达式树lambda可能不包含null传播运算符。
这里的一些讨论:这是一个更简洁的方法。可能(x=>x.Two.Three.Foo);请参见使用表达式树是否会影响性能?执行不同操作是否会改变性能特征?对您或用户会注意到这一点吗?我不知道你的用法,请描述一下。