for
ステートメントを使用して同じ効果を達成できることは知っていますが、C#でforeach
ループを逆方向にループできますか?
リストを使用して作業する場合(直接インデックス付け)、for
ループを使用するほど効率的に行うことはできません。
編集:一般的に、あなたがableでfor
ループを使用する場合、このタスクの正しい方法である可能性が高いことを意味します。さらに、foreach
が順序どおりに実装される限り、構成要素自体は、要素のインデックスと反復順序に依存しないループを表現するために構築されます。これは、 並列プログラミング で特に重要です。 私の意見は、順序に依存する反復はループにforeach
を使用すべきではないということです。
.NET 3.5を使用している場合、これを行うことができます。
IEnumerable<int> enumerableThing = ...;
foreach (var x in enumerableThing.Reverse())
基本的に列挙子をすべて処理してスタックに配置し、すべてを逆順にポップアウトする必要があるため、あまり効率的ではありません。
直接インデックス可能なコレクション(IListなど)がある場合は、代わりにfor
ループを必ず使用する必要があります。
.NET 2.0を使用しており、forループを使用できない場合(つまり、IEnumerableがある場合)、独自のReverse関数を記述する必要があります。これは動作するはずです:
static IEnumerable<T> Reverse<T>(IEnumerable<T> input)
{
return new Stack<T>(input);
}
これは、おそらくそれほど明白ではないいくつかの動作に依存しています。 IEnumerableをスタックコンストラクターに渡すと、IEnumerableを繰り返し処理し、アイテムをスタックにプッシュします。その後、スタックを反復処理すると、逆順にポップアウトします。
これと.NET 3.5 Reverse()
拡張メソッドは、アイテムを返すことを決して止めないIEnumerableをフィードすると明らかに爆発します。
280Z28にあるように、IList<T>
にはインデックスを使用できます。拡張メソッドでこれを隠すことができます:
public static IEnumerable<T> FastReverse<T>(this IList<T> items)
{
for (int i = items.Count-1; i >= 0; i--)
{
yield return items[i];
}
}
これは、最初にすべてのデータをバッファリングするEnumerable.Reverse()
よりも高速です。 (Reverse
にはCount()
のように最適化が適用されるとは思わない。)このバッファリングは、最初に反復を開始したときにデータが完全に読み取られるのに対して、FastReverse
は繰り返しながらリスト。 (繰り返しの間に複数のアイテムを削除した場合にも破損します。)
一般的なシーケンスの場合、逆に反復する方法はありません。たとえば、シーケンスは無限になります。
public static IEnumerable<T> GetStringsOfIncreasingSize()
{
string ret = "";
while (true)
{
yield return ret;
ret = ret + "x";
}
}
それを逆に反復しようとした場合、何が起こると思いますか?
反復に「foreach」を使用する前に、「reverse」メソッドでリストを逆にします。
myList.Reverse();
foreach( List listItem in myList)
{
Console.WriteLine(listItem);
}
List <T>を使用する場合、次のコードも使用できます。
List<string> list = new List<string>();
list.Add("1");
list.Add("2");
list.Add("3");
list.Reverse();
これは、リスト自体を逆に書き込むメソッドです。
今foreach:
foreach(string s in list)
{
Console.WriteLine(s);
}
出力は次のとおりです。
3
2
1
インデックスを作成する余裕がない場合や、Linqクエリの結果を元に戻したい場合や、ソースコレクションを変更したくない場合があります。これらのいずれかが当てはまる場合、Linqが役立ちます。
Linq Selectで匿名型を使用してLinq OrderByDescendingの並べ替えキーを提供するLinq拡張メソッド。
public static IEnumerable<T> Invert<T>(this IEnumerable<T> source)
{
var transform = source.Select(
(o, i) => new
{
Index = i,
Object = o
});
return transform.OrderByDescending(o => o.Index)
.Select(o => o.Object);
}
使用法:
var eable = new[]{ "a", "b", "c" };
foreach(var o in eable.Invert())
{
Console.WriteLine(o);
}
// "c", "b", "a"
「反転」と同義であり、リスト反転の実装とのあいまいさを解消できるため、「反転」という名前が付けられています。
Int32.MinValueとInt32.MaxValueはあらゆる種類のコレクションインデックスの範囲外であるため、コレクションの特定の範囲を逆にすることも可能です。それらを順序付けプロセスに活用できます。要素のインデックスが指定された範囲を下回っている場合、Int32.MaxValueが割り当てられ、OrderByDescendingを使用するときに順序が変わらないようにします。同様に、指定された範囲より大きいインデックスの要素にはInt32.MinValueが割り当てられ、注文プロセスの最後に表示されます。指定された範囲内のすべての要素には通常のインデックスが割り当てられ、それに応じて反転されます。
public static IEnumerable<T> Invert<T>(this IEnumerable<T> source, int index, int count)
{
var transform = source.Select(
(o, i) => new
{
Index = i < index ? Int32.MaxValue : i >= index + count ? Int32.MinValue : i,
Object = o
});
return transform.OrderByDescending(o => o.Index)
.Select(o => o.Object);
}
使用法:
var eable = new[]{ "a", "b", "c", "d" };
foreach(var o in eable.Invert(1, 2))
{
Console.WriteLine(o);
}
// "a", "c", "b", "d"
これらのLinq実装のパフォーマンスヒットが、一時的なリストを使用してコレクションを反転用にラップすることとは対照的です。
この記事を書いている時点では、Linqの独自のReverse実装を認識していませんでしたが、それでもうまくいきました。 https://msdn.Microsoft.com/en-us/library/vstudio/bb358497(v = vs.100).aspx
コレクションコードを変更できる場合は、IEnumerableまたはIEnumerableを実装します(たとえば、IListの独自の実装)。
イテレータ を作成します。たとえば、 IEnumerable インターフェイスを介した次の実装のように、このジョブを実行します(「items」がこのサンプルのListフィールドであると仮定)。
public IEnumerator<TObject> GetEnumerator()
{
for (var i = items.Count - 1; i >= 0; i--)
{
yield return items[i];
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
このため、リストはリストを逆順に繰り返します。
ヒント:ドキュメント内でリストのこの特別な動作を明確に述べる必要があります(StackやQueueのような自己説明的なクラス名を選択することでさらに良くなります)。
いいえ。ForEachは各アイテムのコレクションを反復処理するだけで、IEnumerableまたはGetEnumerator()を使用するかどうかによって順序が異なります。
Jon Skeet によるNiceの回答について少し詳しく説明しますが、これは多用途です:
public static IEnumerable<T> Directional<T>(this IList<T> items, bool Forwards) {
if (Forwards) foreach (T item in items) yield return item;
else for (int i = items.Count-1; 0<=i; i--) yield return items[i];
}
そして、として使用
foreach (var item in myList.Directional(forwardsCondition)) {
.
.
}