最初のコードに明らかではない疑似バグがあります -IEnumerator<T>
拡張IDisposable
するので、それを破棄する必要があります。これは、イテレータ ブロックでは非常に重要です。配列では問題ありませんが、他のIEnumerable<T>
実装では問題になります。
私は次のようにします:
public static IEnumerable<TResult> PairUp<TFirst,TSecond,TResult>
(this IEnumerable<TFirst> source, IEnumerable<TSecond> secondSequence,
Func<TFirst,TSecond,TResult> projection)
{
using (IEnumerator<TSecond> secondIter = secondSequence.GetEnumerator())
{
foreach (TFirst first in source)
{
if (!secondIter.MoveNext())
{
throw new ArgumentException
("First sequence longer than second");
}
yield return projection(first, secondIter.Current);
}
if (secondIter.MoveNext())
{
throw new ArgumentException
("Second sequence longer than first");
}
}
}
その後、必要なときにいつでもこれを再利用できます。
foreach (var pair in columnList.PairUp(currentRow.Split(separatorChar),
(column, value) => new { column, value })
{
// Do something
}
または、ジェネリック ペア型を作成し、ペアアップ メソッドで射影パラメーターを取り除くこともできます。
編集:
Pair 型の場合、呼び出しコードは次のようになります。
foreach (var pair in columnList.PairUp(currentRow.Split(separatorChar))
{
// column = pair.First, value = pair.Second
}
それはあなたが得ることができるのと同じくらい簡単に見えます。はい、再利用可能なコードとして、ユーティリティ メソッドをどこかに配置する必要があります。私の見解ではほとんど問題はありません。複数の配列の場合...
配列が異なるタイプの場合、問題が発生します。ジェネリック メソッド/型宣言で任意の数の型パラメーターを表現することはできません。最大 4 つのデリゲート パラメーターのデリゲートがあるように、必要な数の型パラメーターに対して PairUp のバージョンを作成できますが、可能Action
です。Func
恣意的にしないでください。
ただし、値がすべて同じ型であり、配列にこだわる場合は簡単です。(非配列も問題ありませんが、事前に長さのチェックを行うことはできません。) 次のようにすることができます。
public static IEnumerable<T[]> Zip<T>(params T[][] sources)
{
// (Insert error checking code here for null or empty sources parameter)
int length = sources[0].Length;
if (!sources.All(array => array.Length == length))
{
throw new ArgumentException("Arrays must all be of the same length");
}
for (int i=0; i < length; i++)
{
// Could do this bit with LINQ if you wanted
T[] result = new T[sources.Length];
for (int j=0; j < result.Length; j++)
{
result[j] = sources[j][i];
}
yield return result;
}
}
次に、呼び出しコードは次のようになります。
foreach (var array in Zip(columns, row, whatevers))
{
// column = array[0]
// value = array[1]
// whatever = array[2]
}
もちろん、これには一定量のコピーが必要です-毎回配列を作成しています。次のような別のタイプを導入することで、これを変更できます。
public struct Snapshot<T>
{
readonly T[][] sources;
readonly int index;
public Snapshot(T[][] sources, int index)
{
this.sources = sources;
this.index = index;
}
public T this[int element]
{
return sources[element][index];
}
}
これはおそらくほとんどの人にとってやり過ぎと見なされるでしょう;)
正直に言うと、あらゆる種類のアイデアを思いつき続けることができます...しかし、基本は次のとおりです。
- 少しの再利用可能な作業で、呼び出しコードをより良くすることができます
- タイプの任意の組み合わせの場合、ジェネリックの動作方法により、パラメーターの数 (2、3、4...) ごとに個別に実行する必要があります。
- 各パーツに同じタイプを使用することに満足している場合は、より良いことができます