在 foreach 循环中检查 null

有没有更好的方法来做到以下几点:
在继续循环之前,我需要在文件头上检查 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
}

可能吗?

111933 次浏览

假设 file. Header 中的元素类型是 T,您可以这样做

foreach(var header in file.Headers ?? Enumerable.Empty<T>()){
//do stuff
}

这将创建一个 T 的空枚举。标题为空。但是,如果文件的类型是您所拥有的类型,我会考虑更改 Headers的 getter。null是未知的值,所以如果可能的话,当 null 实际上(/最初)应该被解释为“我不知道是否有任何元素”时,不要使用 null 作为“我知道没有元素”,而是使用一个空集来表示您知道集合中没有元素。这也将是 DRY’er,因为您不必经常进行空检查。

EDIT 作为 Jons 建议的后续,您还可以创建一个扩展方法,将上面的代码更改为

foreach(var header in file.Headers.OrEmptyIfNull()){
//do stuff
}

在无法更改 getter 的情况下,这将是我自己的首选,因为它通过给操作命名(OrEmptyIfNull)来更清楚地表达意图

上面提到的扩展方法可能使某些优化不可能被优化器检测到。具体来说,可以消除那些使用方法重载它的与 IList 相关的内容

public static IList<T> OrEmptyIfNull<T>(this IList<T> source)
{
return source ?? Array.Empty<T>();
}

迭代之前的“如果”是可以的,很少有那些“漂亮”的语义可以使代码不易读。

无论如何,如果压痕干扰了你,你可以改变如果检查:

if(file.Headers == null)
return;

只有当头部属性中有一个真值时,才能进入 foreach 循环。

我可以考虑的另一个选项是在 foreach 循环中使用 null 聚合运算符,从而完全避免 null 检查。 样本:

List<int> collection = new List<int>();
collection = null;
foreach (var i in collection ?? Enumerable.Empty<int>())
{
//your code here
}

(用真正的对象/类型替换集合)

作为对 鲁内的建议的一个小小的美化,你可以创建你自己的扩展方法:

public static IEnumerable<T> OrEmptyIfNull<T>(this IEnumerable<T> source)
{
return source ?? Enumerable.Empty<T>();
}

然后你可以写:

foreach (var header in file.Headers.OrEmptyIfNull())
{
}

根据口味更改名称:)

对于这些场景,我使用了一个不错的小扩展方法:

  public static class Extensions
{
public static IList<T> EnsureNotNull<T>(this IList<T> list)
{
return list ?? new List<T>();
}
}

假设 Header 是类型 list,您可以执行以下操作:

foreach(var h in (file.Headers.EnsureNotNull()))
{
//do stuff
}

坦白地说,我建议: 只要吸收 null测试。null测试是 只是brfalse或者 brfalse.s; 其他的一切都将涉及更多的工作(测试、赋值、额外的方法调用、迭代器上不必要的 GetEnumerator()MoveNext()Dispose()等等)。

if测试是简单、明显和有效的。

对于某些情况,我更倾向于另一种类型的变体,假设默认的集合构造函数返回空实例。

最好将这个方法命名为 NewIfDefault。它不仅对集合有用,因此类型约束 IEnumerable<T>可能是多余的。

public static TCollection EmptyIfDefault<TCollection, T>(this TCollection collection)
where TCollection: class, IEnumerable<T>, new()
{
return collection ?? new TCollection();
}

使用比标准 ForEach 循环运行速度更快的 空条件运算符和 ForEach ()。
但是必须将集合强制转换为 List。

   listOfItems?.ForEach(item => // ... );

2022年的最佳答案应该是:

foreach (var h in file.Headers ?? Enumerable.Empty<T>())
{
//do stuff
}

T替换为数据类型.if file.Header 是一个数组,使用 Array.Empty<T>()而不是 Enumerable.Empty<T>()