您的初始代码中有一个不明显的伪错误 -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 版本,就像最多 4 个委托参数的委托一样 - 但您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...)
- 如果你乐于为每个部分使用相同的类型,你可以做得更好