您的初始代码中有一个不明显的伪错误 - 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))
{
// column = pair.First, value = pair.Second
}
这看起来很简单。是的,您需要将实用程序方法放在某个地方,作为可重用代码。在我看来几乎不是问题。现在对于多个数组...
如果数组是不同类型的,我们就有问题了。您不能在泛型方法/类型声明中表达任意数量的类型参数 - 您可以为任意数量的类型参数编写 PairUp 版本,就像 Action 和 Func 代表最多 4委托参数 - 但您不能随意设置。
但是,如果这些值都属于同一类型 - 如果您乐于坚持使用数组 - 这很容易。 (非数组也可以,但你不能提前检查长度。)你可以这样做:
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...)
- 如果您乐于为每个部件使用相同的类型,您可以做得更好