在 foreach 循环中检查 null
Posted
技术标签:
【中文标题】在 foreach 循环中检查 null【英文标题】:Check for null in foreach loop 【发布时间】:2012-07-28 21:09:06 【问题描述】:是否有更好的方法来执行以下操作: 在继续循环之前,我需要在 file.Headers 上检查 null
if (file.Headers != null)
foreach (var h in file.Headers)
//set lots of properties & some other stuff
简而言之,由于我的代码中发生的缩进级别,在 if 中编写 foreach 看起来有点难看。
会评估为
foreach(var h in (file.Headers != null))
//do stuff
可能吗?
【问题讨论】:
你可以看这里:***.com/questions/6937407/… ***.com/questions/872323/… 是另一个想法。 @AdrianFaciu 我认为这完全不同。该问题在执行 for-each 之前首先检查集合是否为空。您的链接检查集合中的项目是否为空。 类似于***.com/q/3088147/80161 和***.com/q/6455311/80161 C# 8 可以简单地具有某种空条件 foreach,即这样的语法:foreach? (var i in collection) 我认为这是一个足够常见的场景来证明这一点,并且鉴于最近对语言的空条件添加,它在这里有意义吗? 【参考方案1】:作为对 Rune 建议的轻微修饰,您可以创建自己的扩展方法:
public static IEnumerable<T> OrEmptyIfNull<T>(this IEnumerable<T> source)
return source ?? Enumerable.Empty<T>();
然后你可以写:
foreach (var header in file.Headers.OrEmptyIfNull())
根据口味改名:)
【讨论】:
【参考方案2】:假设 file.Headers 中元素的类型是 T 你可以这样做
foreach(var header in file.Headers ?? Enumerable.Empty<T>())
//do stuff
如果 file.Headers 为空,这将创建一个空的 T 枚举。但是,如果文件类型是您拥有的类型,我会考虑更改 Headers
的 getter。 null
是 unknown 的值,所以如果可能,当 null 实际上(/最初)应该解释为“我不知道是否有任何元素”时,而不是使用 null 作为“我知道没有元素”,而是使用空设置以表明您知道集合中没有元素。这也将是 DRY'er,因为您不必经常进行 null 检查。
EDIT 作为 Jons 建议的后续,您还可以创建一个扩展方法,将上述代码更改为
foreach(var header in file.Headers.OrEmptyIfNull())
//do stuff
在你不能改变getter的情况下,这将是我自己的首选,因为它通过给操作命名(OrEmptyIfNull)更清楚地表达了意图
上面提到的扩展方法可能会使优化器无法检测到某些优化。具体来说,那些与 IList 相关的使用方法重载 this 可以消除
public static IList<T> OrEmptyIfNull<T>(this IList<T> source)
return source ?? Array.Empty<T>();
【讨论】:
@kjbartel 的回答(在“***.com/a/32134295/401246”是最好的解决方案,因为它不会:a)涉及性能下降(即使不是null
)退化IEnumerable<T>
到 LCD 的整个循环(如使用 ?? 会),b)需要向每个项目添加扩展方法,或 c)需要避免 null
IEnumerables
(Pffft!Puh-LEAZE!SMH。)以 (cuz null
表示 N/A,而空列表表示,它适用,但目前,嗯,为空!,即员工可能有非销售的 N/A 的佣金或销售空的佣金,但他们没有' t 赚取任何)。
@Tom 除了 one 空检查之外,对于枚举数不为空的情况没有任何惩罚。在确保可枚举不为空的同时避免该检查是不可能的。上面的代码需要 IEnumerable
的标头,这比 foreach
要求更严格,但比您链接到的答案中List<T>
的要求更严格。测试可枚举是否为空的性能损失相同。
我将“LCD”问题基于 Eric Lippert 对 Vlad Bezden 的回答与 kjbartel 的回答在同一线程中的评论:“@CodeInChaos:啊,我现在明白你的意思了。当编译器可以检测到“foreach”正在迭代 ListList<T>
。坦率地说,我建议:接受null
测试。 null
测试只是 brfalse
或brfalse.s
;其他一切都将涉及更多工作(测试、分配、额外的方法调用、迭代器上不必要的GetEnumerator()
、MoveNext()
、Dispose()
等)。
if
测试简单、明显且高效。
【讨论】:
您确实提出了一个有趣的观点 Marc,但是,我目前正在寻求减少代码的缩进级别。但是当我需要记录性能时,我会记住您的评论。 在我提出这个问题并需要实施一些性能增强多年后,就这个 Marc 做一个简短的说明,您的建议非常方便。谢谢【参考方案4】:使用Null-conditional Operator 和 ForEach() 比标准的 foreach 循环更快。 不过,您必须将集合转换为 List。
listOfItems?.ForEach(item => // ... );
【讨论】:
请在您的答案周围添加一些解释,明确说明此解决方案为何有效,而不仅仅是代码单行 最适合我的情况【参考方案5】:迭代前的“if”没问题,那些“漂亮”的语义很少会降低您的代码的可读性。
无论如何,如果缩进打扰了你,你可以改变 if 来检查:
if(file.Headers == null)
return;
只有当 headers 属性有一个真值时,你才会进入 foreach 循环。
我可以考虑的另一个选项是在 foreach 循环中使用 null-coalescing 运算符并完全避免 null 检查。 示例:
List<int> collection = new List<int>();
collection = null;
foreach (var i in collection ?? Enumerable.Empty<int>())
//your code here
(将集合替换为您的真实对象/类型)
【讨论】:
如果if语句之外有任何代码,您的第一个选项将不起作用。 我同意,与创建新列表相比,if 语句实现起来简单且便宜,只是为了美化代码。【参考方案6】:我正在为这些场景使用一个不错的小扩展方法:
public static class Extensions
public static IList<T> EnsureNotNull<T>(this IList<T> list)
return list ?? new List<T>();
鉴于 Headers 是列表类型,您可以执行以下操作:
foreach(var h in (file.Headers.EnsureNotNull()))
//do stuff
【讨论】:
您可以使用??
运算符并将return 语句缩短为return list ?? new List<T>;
@wolfgangziegler,如果我理解正确,您的样本file.Headers.EnsureNotNull() != null
中的null
测试是不需要的,甚至是错误的?【参考方案7】:
在某些情况下,我更喜欢另一种通用变体,假设默认集合构造函数通常返回空实例。
最好将此方法命名为NewIfDefault
。它不仅对集合有用,所以类型约束IEnumerable<T>
可能是多余的。
public static TCollection EmptyIfDefault<TCollection, T>(this TCollection collection)
where TCollection: class, IEnumerable<T>, new()
return collection ?? new TCollection();
【讨论】:
以上是关于在 foreach 循环中检查 null的主要内容,如果未能解决你的问题,请参考以下文章