你的初始代码中存在一个不明显的伪错误-
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类型,并在PairUp方法中去掉投影参数。
编辑:
使用Pair类型,调用代码将如下所示:
foreach (var pair in columnList.PairUp(currentRow.Split(separatorChar))
{
}
看起来这就是最简单的方式。是的,你需要把这个实用方法放在某个地方以便重复使用。在我看来这不是问题。现在考虑多个数组的情况...
如果这些数组是不同类型的,我们会遇到问题。你不能在泛型方法/类型声明中表达任意数量的类型参数 —— 你可以为想要的任意数量的类型参数编写 PairUp 的版本,就像有 Action 和 Func 委托可以处理多达4个委托参数一样 —— 但你无法使其成为任意的。
然而,如果所有的值都是相同类型的,并且你愿意坚持使用数组,那么很容易。(非数组也可以,但你不能提前检查长度。)你可以这样做:
public static IEnumerable<T[]> Zip<T>(params T[][] sources)
{
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++)
{
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))
{
}
当然,这涉及到一定的复制 - 每次创建一个数组。您可以通过引入另一种类型来改变它,如下所示:
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...)。
- 如果你愿意为每个部分使用相同的类型,你可以做得更好。