foreach内にループカウントインデックスを設定し、整数を作成し、それを使用し、インクリメントする必要がある場合、プログラミング中にこれに頻繁に出くわします。 foreach内のループ数?他のループでも使用できます。
これは、コンパイラーがこのキーワードをループ構造内以外のどこで使用することをどのように許可しないかというキーワードの使用に反しますか?
for ()
ループを使用するだけです。
foreach内にループカウントインデックスを設定し、整数を作成し、それを使用し、インクリメントする必要がある場合、プログラミング中にこれに頻繁に出くわします。 foreach内のループ数?他のループでも使用できます。
これは、コンパイラーがこのキーワードをループ構造内以外のどこで使用することをどのように許可しないかというキーワードの使用に反しますか?
for ()
ループを使用するだけです。
回答:
ループ内でループカウントが必要な場合foreach
は、通常のfor
ループを使用しないでください。foreach
ループは、特定の用途にすることを意図されたfor
シンプルなループを。のシンプルさがforeach
もはや有益ではない状況があるようです。
IEnumerable.Count
高価になる可能性があります(または、1回だけの列挙の場合は不可能です)。安全にそれを行う前に、基礎となるソースが何かを知る必要があります。
foreach
ていfor (int i = 0…)
ます。彼は数ページ書いていますが、私は2つの言葉で要約できます-反復時のコンパイラー最適化。それは2語ではありませんでした。
for
ループがより読みやすいコードを提供するOPで説明されているような十分な状況を見てきました(そして理論的なコンパイラの最適化は、多くの場合、時期尚早な最適化です)。
このような匿名型を使用できます
foreach (var item in items.Select((v, i) => new { Value = v, Index = i }))
{
Console.WriteLine("Item value is {0} and index is {1}.", item.Value, item.Index);
}
これはPythonのenumerate
関数に似ています
for i, v in enumerate(items):
print v, i
リクエストに応じて、ダンフィンチの答えをここに追加します。
私にポイントを与えないでください、ダン・フィンチにポイントを与えてください:-)
解決策は、次の一般的な拡張メソッドを記述することです。
public static void Each<T>( this IEnumerable<T> ie, Action<T, int> action )
{
var i = 0;
foreach ( var e in ie ) action( e, i++ );
}
これは次のように使用されます:
var strings = new List<string>();
strings.Each( ( str, n ) =>
{
// hooray
} );
私はこれについて間違っているかもしれませんが、foreachループの主なポイントはC ++のSTL日からの反復を単純化することだといつも思っていました。
for(std::stltype<typename>::iterator iter = stl_type_instance.begin(); iter != stl_type_instance.end(); iter++)
{
//dereference iter to use each object.
}
これをforeachでの.NETのIEnumerableの使用と比較してください。
foreach(TypeName instance in DescendentOfIEnumerable)
{
//use instance to use the object.
}
後者ははるかに単純で、古い落とし穴の多くを回避します。さらに、彼らはほとんど同じルールに従っているようです。たとえば、ループ内ではIEnumerableの内容を変更することはできません(これをSTLの方法で考えると、はるかに理にかなっています)。ただし、forループの多くの場合、論理的な目的は反復とは異なります。
for(IEnumerator e=collection.GetEnumerator;e.MoveNext();) { ... }
問題のコレクションがである場合は、IList<T>
単にfor
インデックス付けで使用できます。
for (int i = 0; i < collection.Count; i++)
{
var item = collection[i];
// …
}
そうでない場合は、を使用できますSelect()
。インデックスを提供するオーバーロードがあります。
それも適切でない場合、手動でインデックスを維持することは非常に簡単であると思います。これは非常に短い2行のコードです。このためのキーワードを作成するのはやり過ぎです。
int i = 0;
foreach (var item in collection)
{
// …
i++;
}
foo(++i)
またはそれにfoo(i++)
応じて、何かを実行できます。
コレクションがIEnumerableであることがわかっているが、これまでに処理した要素の数(つまり、完了したときの合計)を追跡する必要がある場合は、基本的なforループに数行追加できます。
var coll = GetMyCollectionAsAnIEnumerable();
var idx = 0;
for(var e = coll.GetEnumerator(); e.MoveNext(); idx++)
{
var elem = e.Current;
//use elem and idx as you please
}
また、インクリメントされたインデックス変数をforeachに追加することもできます。
var i=0;
foreach(var elem in coll)
{
//do your thing, then...
i++;
}
これをよりエレガントに見せたい場合は、1つまたは2つの拡張メソッドを定義して、これらの詳細を「隠す」ことができます。
public static void ForEach<T>(this IEnumerable<T> input, Action<T> action)
{
foreach(T elem in input)
action(elem);
}
public static void ForEach<T>(this IEnumerable<T> input, Action<T, int> action)
{
var idx = 0;
foreach(T elem in input)
action(elem, idx++); //post-increment happens after parameter-passing
}
//usage of the index-supporting method
coll.ForEach((e, i)=>Console.WriteLine("Element " + (i+1) + ": " + e.ToString()));