是否可以同时迭代两个IEnumerable对象?

时间:2009-06-11 15:37:55

标签: vb.net visual-studio-2005 .net-2.0

如果我有一个List(Of x)和一个List(Of y),是否可以同时迭代这两个?

这样的东西
for each _x as X, _y as Y in List(of x), List(of y) 
    if _x.item = _y.item then
        'do something
    end if
next

这些列表可能有不同的大小。

我正在使用.Net2.0,我怀疑这是我的垮台,因为我有一种感觉LINQ可以通过加入常见id上的列表轻松解决问题。

7 个答案:

答案 0 :(得分:5)

你可以使用老式的for循环。像

这样的东西
For ii As Integer = 0 To Math.Min(list1.Count, list2.Count)
    If list1(ii) = list2(ii) Then
    End If
Next

答案 1 :(得分:4)

IIRC,.Net 4.0将为IEnumerable提供.Zip()扩展方法,已经这样做了。

与此同时,建立自己的并不难。令人惊讶的是,虽然其他几个答案非常接近,但他们都至少有一个问题。希望他们会得到纠正。在此期间,这应该在VB.Net中使用强类型枚举器,使用未知类型的正确比较,并正确处理枚举器,来执行您想要的操作:

Using xe As IEnumerator(Of X) = List1.GetEnumerator(), _
      ye As IEnumerator(Of Y) = List2.GetEnumerator()

    While xe.MoveNext() AndAlso ye.MoveNext() 
        If xe.Current.Equals(ye.Current) Then
            ''// do something
        End If
    End While
End Using

现在让我们把它放到一个函数中,你可以将自己的代理传递给:

Public Shared Sub ZipAction(Of X, Y)(ByVal source1 As IEnumerable(Of X), ByVal source2 As IEnumerable(Of Y), _
                              ByVal compare As Func(Of X, Y, Boolean), Byval OnEquals As Action(Of X, Y))  

    Using xe As IEnumerator(Of X) = source1.GetEnumerator(), _
          ye As IEnumerator(Of Y) = source2.GetEnumerator()

        While xe.MoveNext() AndAlso ye.MoveNext() 
            If compare(xe.Current, ye.Current) Then
                OnEquals(xe.Current, ye.Current)
            End If
        End While
    End Using
End Sub

最后,由于这些委托类型在.Net 3.5之前不可用,您可以像这样在.Net 2.0中轻松声明它们:

Public Delegate Sub Action(Of T1, T2) ( _
    arg1 As T1, _
    arg2 As T2 _
)

Public Delegate Function Func(Of T1, T2, TResult) ( _
    arg1 As T1, _
    arg2 As T2, _
) As TResult

要使用此代码,您可以执行以下操作:

Public Class X
    Public Item As String
    ''//...
End Class

Public Class Y
    Public Item As String
    ''//...
End Class

Public Class Test

    Private Function CompareXtoY(ByVal arg1 As X, ByVal arg2 As Y) As Boolean
        Return arg1.Item = arg2.Item
    End Function

    Private Sub OnList1ItemMatchesList2Item(ByVal arg1 As X, ByVal arg2 As Y)
        ''// Do something...
    End Sub

    Private list1 As List(Of X) = GetXList()
    Private list2 As List(Of Y) = GetYList()

    Public Sub TestZip()
        ZipAction(list1, list2, AddressOf CompareXtoY, AddressOf OnList1ItemMatchesList2Item)
    End Sub

End Class

如果这是C#,我会让函数成为迭代器块,并且每个匹配对都“返回”,而不是要求你传递一个Action委托。

答案 2 :(得分:2)

不,不是在vb.net中使用vb循环结构。

然而,您可以使用枚举器自行完成:

    Sub MyOwnIENumeration()
    Dim a As List(Of String), b As List(Of String)
    Dim EnmA As System.Collections.Generic.IEnumerator(Of String) = a.GetEnumerator
    Dim EnmB As System.Collections.Generic.IEnumerator(Of String) = b.GetEnumerator

    If EnmA.MoveNext() And EnmB.MoveNext() Then
        Do
            If EnmA.Current = EnmB.Current Then
                Debug.Print("list matched on " & EnmA.Current)
                If Not EnmA.MoveNext() Then Exit Do
                If Not EnmB.MoveNext() Then Exit Do
            ElseIf EnmA.Current < EnmB.Current Then
                If Not EnmA.MoveNext() Then Exit Do
            Else
                If Not EnmB.MoveNext() Then Exit Do
            End If
        Loop
    End If

    EnmA.Dispose() : EnmB.Dispose()
End Sub

答案 3 :(得分:2)

您必须手动访问枚举器。在C#中:

using (IEnumerator<X> xe = List1.GetEnumerator())
using (IEnumerator<Y> ye = List2.GetEnumerator()) {
    while (xe.MoveNext() && ye.MoveNext()) {
         if (xe.Current == ye.Current) {
             // do something
         }
    }
}

答案 4 :(得分:0)

你的意思是没有做一个嵌套循环?

foreach _x as X in List(of x)
     foreach _y as Y in List(of y)
          if _x.item = _y.item then
               'do something
          end if
 next

不是VB程序员,所以我的语法可能不对,但你明白了。

答案 5 :(得分:0)

我对VB.NET不是很精通,但是这是C#中的一个扩展方法,你可以创建它来做到这一点。我希望你能移植它!

public static class IEnumExtensions
{
    public static IEnumerable<KeyValuePair<T, U>> EnumTwoCollections<T, U>(this IEnumerable<T> list1, IEnumerable<U> list2)
    {
        var enumerator1 = list1.GetEnumerator();
        var enumerator2 = list2.GetEnumerator();

        bool moveNext1 = enumerator1.MoveNext();
        bool moveNext2 = enumerator2.MoveNext();

        while (moveNext1 || moveNext2)
        {

            T tItem = moveNext1 ? enumerator1.Current : default(T);
            U uItem = moveNext2 ? enumerator2.Current : default(U);

            yield return new KeyValuePair<T, U>(tItem, uItem);

            moveNext1 = enumerator1.MoveNext();
            moveNext2 = enumerator2.MoveNext();
        }
    }
}

用法:

    List<int> intList = new List<int>();
    List<string> stringList = new List<string>();
    for (int i = 1; i <= 10; i++)
    {
        intList.Add(i);
        stringList.Add((i * i).ToString());
    }

    foreach (var items in intList.EnumTwoCollections(stringList))
    {
        Console.WriteLine(items.Key + " , " + items.Value);
    }

现在注意到您没有使用.NET 3.5,因此您可能也无法使用扩展方法。把它放在一个帮助类中你就可以这样调用它:

foreach(KeyValuePair<int,string> kvp in Helper.EnumTwoCollections(intList,stringList))
{
...
}

答案 6 :(得分:0)

您可以使用每个IEnumerable的GetEnumerator()函数来完成它。

您可以执行以下操作:

 // Assume List1 and List2 are IEnumerable variables
 Dim list1 As IEnumerator = List1.GetEnumerator()
 Dim list2 As IEnumerator = List2.GetEnumerator();
 While list1.MoveNext() And list2.MoveNext()
      If list1.Current = list2.Current Then
          // Do Something
      End If
 End While

以下是来自MSDN

的更多信息