Previous | Index | Next 

[PRB] .NET collections can’t be modified while inside a For Each loop

VB6 and VB.NET collections differ in a substantial way: you can’t modify a VB.NET collection (or any .NET collection, for that matter) from inside a For Each loop that is iterating over the collection itself. You see the problem in the following piece of code, which is quite common in VB6:

        ' unload all forms
        Dim frm As Form
        For Each frm In Forms
            Unload frm
        Next

The code is translated almost literally to .NET:

        ' VB.NET
        Dim frm As VB6Form
        For Each frm In Forms6
            Unload6(frm)
        Next
        
         // C#
	foreach (VB6Form frm in VBProject.Forms)
	{
		VB6Helpers.Unload(frm);
	}

When you run the above code, the Next statement throws an InvalidOperationException error whose message is quite self-explanatory:

Collection was modified; enumeration operation may not execute.

VB Migration Partner doesn’t fix this issue automatically. However, you can edit the original VB6 code to use the CloneCollection6 method defined in VBMigrationPartner_Support.bas module:

        ' unload all forms
        Dim frm As Form
        For Each frm In CloneCollection6(Forms)
            Unload frm
        Next

When converting to C#, you have slightly modify the code so that it uses the method defined in the VB6Helpers class:

        
         // C#
	foreach (VB6Form frm in VB6Helpers.CloneCollection(VBProject.Forms))
	{
		VB6Helpers.Unload(frm);
	}

The VB6 version of the CloneCollection6 method simply returns its argument, therefore the original program isn’t affected in any way. The .NET version of the method (defined in the support library) creates and returns a copy of the original collection, therefore the loop iterates on a collection instance other than the instance being modified in the loop itself and no runtime error occurs.

In a few special cases you might prefer not to create a clone of the original collection, for example if the collection has too many elements and the copy operation would add a noticeable overhead. In such special cases, you can avoid the issue by replacing the For Each block with a regular For loop, as in:

        Dim i As Integer
        For i = Forms.Count – 1 To 0 Step -1 
            Unload Forms(i)
        Next

or

        Dim i As Integer
        For i = 1 To Forms.Count
            Unload Forms(0)
        Next

 

Previous | Index | Next