if(items!= null)がforeach(T item in items)の前に不必要ですか?


103

私はしばしば次のようなコードに出くわします:

if ( items != null)
{
   foreach(T item in items)
   {
        //...
   }
}

基本的に、if条件は、がnullでないforeach場合にのみブロックが実行されることを保証しitemsます。if条件が本当に必要かどうか、または場合foreachを処理しますかitems == null

つまり、簡単に書けますか

foreach(T item in items)
{
    //...
}

itemsnull かどうかを心配せずに?あるif条件の余分は?それともこれは、依存するタイプitems上で多分かTにも?



1
@kjbartelの答え(「stackoverflow.com/a/32134295/401246」は最適なソリューションです。なぜなら、次のことを行わないためです。a null)ループ全体をLCDに一般化する(そうでない場合でも)パフォーマンスの低下を伴うEnumerable(使用??するように) )、b)すべてのプロジェクトに拡張メソッドを追加する必要がある、またはc)null IEnumerables(Pffft!Puh-LEAZE!SMH)で始まることを避ける必要がある(cuz、nullN / Aを意味するが、空のリストはそれがアプリケーションであることを意味するが、現在、まあ、空の!、つまり、Empl。は、非セールスの場合はN / Aであるコミッション、またはセールスがない場合は空のコミッションを持つことができます)。
トム

回答:


115

(items!= null)かどうかを確認する必要があります。そうしないと、NullReferenceExceptionが発生します。ただし、次のようなことができます。

List<string> items = null;  
foreach (var item in items ?? new List<string>())
{
    item.Dump();
}

しかし、あなたはそれのパフォーマンスをチェックするかもしれません。したがって、私はまだif(items!= null)を最初に持つことを好みます。

エリックのリッパートの提案に基づいて、コードを次のように変更しました。

List<string> items = null;  
foreach (var item in items ?? Enumerable.Empty<string>())
{
    item.Dump();
}

31
かわいいアイデア; 空の配列は、メモリの消費量が少なく、メモリの負荷が少ないため、望ましいです。Enumerable.Empty <string>は、生成する空の配列をキャッシュしてそれを再利用するため、さらに好ましいでしょう。
Eric Lippert、2011年

5
2番目のコードは遅くなると思います。それはシーケンスを縮退させIEnumerable<T>、次に、インターフェースへの列挙子に低下させ、反復を遅くします。私のテストでは、int配列での反復に対して、ファクター5の低下が見られました。
CodesInChaos

11
@CodeInChaos:通常、空のシーケンスを列挙する速度がプログラムのパフォーマンスのボトルネックであると思いますか?
Eric Lippert、2011年

14
空のシーケンスの列挙速度だけでなく、完全なシーケンスの列挙速度も低下させます。そして、シーケンスが十分に長い場合、それは問題になる可能性があります。ほとんどのコードでは、慣用的なコードを選択する必要があります。しかし、あなたが言及した2つの割り当ては、さらに少ないケースではパフォーマンスの問題になります。
CodesInChaos

15
@CodeInChaos:ああ、私は今あなたの主張を理解しています。コンパイラは、「foreach」がList <T>または配列を反復処理していることを検出できる場合、値タイプの列挙子を使用するようにforeachを最適化するか、「for」ループを実際に生成できます。リストまたは空のシーケンスのいずれかを列挙することを余儀なくされた場合、「最も低い共通分母」のcodegenに戻る必要があります。これは、場合によっては遅くなり、より多くのメモリプレッシャーを生み出します。これは微妙ですが優れた点です。もちろん、この話の教訓は-いつものように-パフォーマンスの問題がある場合は、それをプロファイリングして実際のボトルネックが何であるかを調べます。
Eric Lippert、2011年

68

C#6を使用すると、新しいnull条件演算子をList<T>.ForEach(Action<T>)(または独自のIEnumerable<T>.ForEach拡張メソッド)と共に使用できます。

List<string> items = null;
items?.ForEach(item =>
{
    // ...
});

エレガントな答え。ありがとう!
スティーブ

2
これは最適なソリューションです。それは、a)(そうでない場合でもnull)ループ全体をLCDに一般化するパフォーマンス低下を伴うEnumerable(使用??するように)、b)すべてのプロジェクトに拡張メソッドを追加する必要がある、またはc )null IEnumerable(Pffft!Puh-LEAZE!SMH。)の先頭に(cuz nullはN / Aを意味しますが、空のリストはそれが適用されることを意味しますが、現在はです!非セールスの場合はN / A、獲得していない場合はセールスの場合は空)。
トム

6
@Tom:それは、単なるanyではなくitems、seであると想定してList<T>いますIEnumerable<T>。(または、カスタム拡張メソッドがあり、そこにしたくないと言った...)さらに、基本的に特定の回答が好きだと言っている11のコメントをすべて追加する価値はないと私は言います。
Jon Skeet

2
@トム:今後はそうしないように強くお勧めします。あなたのコメントに反対したすべての人があなたのコメントすべてにコメントを追加したと想像してください。(ここで返信を11回書いたとしましょう。)これは単にスタックオーバーフローの生産的な使用ではありません。
Jon Skeet、

1
また、標準ではなくデリゲートを呼び出すとパフォーマンスに影響が出ると思いますforeach。特に、forループに変換されると思うリストの場合。
kjbartel

37

ここでの本当の要点は、最初はシーケンスがnullになることはほとんどないはずです。すべてのプログラムでそれを不変にするだけで、シーケンスがある場合はnullになりません。常に初期化されて、空のシーケンスまたはその他の正規のシーケンスになります。

シーケンスがnullになることがない場合、明らかにそれをチェックする必要はありません。


1
WCFサービスからシーケンスを取得する場合はどうでしょうか。ヌルかもしれませんね。
Nawaz、2011年

4
@Nawaz:空のシーケンスになるようにnullシーケンスを返すWCFサービスがあった場合は、バグとして報告します。とはいえ、もしバグのあるサービスの不適切な形式の出力を処理する必要がある場合は、はい、nullをチェックして対処する必要があります。
Eric Lippert、2011年

7
もちろん、nullとemptyがまったく異なることを意味しない限り。時々それはシーケンスに有効です。
コンフィギュレー

@Nawaz空のコレクションの代わりにnullを返すDataTable.Rowsはどうでしょうか。多分それはバグですか?
Neil B

@kjbartelの答え(「stackoverflow.com/a/32134295/401246」は最適なソリューションです。なぜなら、次のことを行わないためです。a null)ループ全体をLCDに一般化する(そうでない場合でも)パフォーマンスの低下を伴うEnumerable(使用??するように) )、b)すべてのプロジェクトに拡張メソッドを追加する必要がある、またはc)null IEnumerables(Pffft!Puh-LEAZE!SMH)で始まることを避ける必要がある(cuz、nullN / Aを意味するが、空のリストはそれがアプリケーションであることを意味するが、現在、まあ、空の!、つまり、Empl。は、非セールスの場合はN / Aであるコミッション、またはセールスがない場合は空のコミッションを持つことができます)。
トム

10

実際にはその@Connectに機能リクエストがあります:http ://connect.microsoft.com/VisualStudio/feedback/details/93497/foreach-should-check-for-null

そして、応答は非常に論理的です:

ほとんどのforeachループは、null以外のコレクションを反復する目的で記述されていると思います。nullを反復処理しようとすると、例外が発生するため、コードを修正できます。


これには賛否両論あると思いますので、そもそも設計通りにしておくことにしました。結局のところ、foreachは単なる構文上の砂糖です。items.GetEnumerator()を呼び出した場合、itemsがnullの場合もクラッシュするため、最初にテストする必要がありました。
Marius Bancila

6

あなたはいつもnullリストでそれをテストすることができます...しかし、これは私がmsdnウェブサイトで見つけたものです

foreach-statement:
    foreach   (   type   identifier   in   expression   )   embedded-statement 

式の値がnullの場合、System.NullReferenceExceptionがスローされます。


2

余計なものではありません。実行時に項目はIEnumerableにキャストされ、そのGetEnumeratorメソッドが呼び出されます。それは失敗するアイテムの逆参照を引き起こします


1
1)シーケンスは必ずしもキャストされるわけではありませんIEnumerable。2)シーケンスをスローさせるのは設計上の決定です。C#はnull、開発者がそれを良いアイデアと見なした場合、そのチェックを簡単に挿入できます。
CodesInChaos

2

拡張メソッドでnullチェックをカプセル化し、ラムダを使用できます。

public static class EnumerableExtensions {
  public static void ForEach<T>(this IEnumerable<T> self, Action<T> action) {
    if (self != null) {
      foreach (var element in self) {
        action(element);
      }
    }
  }
}

コードは次のようになります。

items.ForEach(item => { 
  ...
});

アイテムを受け取って返すメソッドを呼び出すだけの場合は、さらに簡潔にすることができますvoid

items.ForEach(MethodThatTakesAnItem);

1

これは必要です。foreachコンテナにアクセスしてイテレーションを設定すると、例外が発生します。

カバーの下に、foreach使用するコレクションクラスに実装されたインタフェースを反復処理を実行します。一般的な同等のインターフェースはこちらです。

C#言語のforeachステートメント(Visual Basicではそれぞれに対して)は、列挙子の複雑さを隠します。したがって、列挙子を直接操作するのではなく、foreachを使用することをお勧めします。


1
ただ、それは技術的にインタフェースを使用していないノートとして、それはアヒルタイピングを使用しています:blogs.msdn.com/b/kcwalina/archive/2007/07/18/ducknotation.aspxインタフェースは、右のメソッドとプロパティがあることを確認してくださいただし、意図の理解に役立ちます。外部のforeachと同様に使用
ShuggyCoUk

0

コレクションがnullの場合、foreachはNullReferenceExceptionをスローするため、テストが必要です。実際に試すのはとても簡単です。

List<string> items = null;
foreach(var item in items)
{
   Console.WriteLine(item);
}

0

2番目はNullReferenceExceptionメッセージをスローしますObject reference not set to an instance of an object.


0

前述のように、ここであなたはそれがnullでないか確認する必要があります。

nullと評価される式は使用しないでください。


0

C#6では、次のようにsthを記述できます。

// some string from file or UI, i.e.:
// a) string s = "Hello, World!";
// b) string s = "";
// ...
var items = s?.Split(new char[] { ',', '!', ' ' }) ?? Enumerable.Empty<string>();  
foreach (var item in items)
{
    //..
}

基本的にはVlad Bezdenのソリューションですが、?? nullではない配列を常に生成する式。これにより、foreachブラケット内でこのチェックが行われるのではなく、foreachが存続します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.