0

In the code below, when trying to remove an item from the Cases list the code breaks in the Setter with an index out of bounds. When running the debugger in VisualStudio 2017 it successfully goes through the Remove() function and deletes the last item but after returning to Main() it will break on the Setter and the call stack says it is coming from the Remove call. Example code below:

Sub Main()
    Dim Cases As Collection = New Collection()
    Dim caseIndex As Integer = 2
    Cases.Remove(Cases(caseIndex))
End Sub

Public Class Collection

    Public WithEvents Cases As List(Of CaseClass)

    Public Sub New()
        Cases = New List(Of CaseClass)()
        Cases.Add(New CaseClass)
        Cases.Add(New CaseClass)
        Cases.Add(New CaseClass)
    End Sub

    Default Public Property BeltCase(ByVal Index As Integer) As CaseClass
        Get
            Return Cases(Index)
        End Get
        Set(ByVal Value As CaseClass)
            Cases(Index) = Value
        End Set
    End Property

    Public Sub Remove(ByRef BeltCase As CaseClass)
        Cases.Remove(BeltCase)
    End Sub
End Class

Public Class CaseClass
    Public test As Int16
End Class

Call Stack:

TestingVBBug.exe!TestingVBBug.Module1.Collection.set_BeltCase(Integer Index,TestingVBBug.Module1.CaseClass Value) Line 25 Basic TestingVBBug.exe!TestingVBBug.Module1.Main() Line 6 Basic

So why would we be going through the Setter at all. And why does that happen after we exit the remove function?

user3565590
  • 167
  • 1
  • 11
  • 1
    See the article [The many cases of ByRef](https://blogs.msdn.microsoft.com/jaredpar/2010/01/21/the-many-cases-of-byref/) to learn that VB allows you to pass a Property ByRef which is the root of your problem. – TnTinMn Jun 12 '18 at 19:45

1 Answers1

2

The problem is caused by your Remove() method, that is, you have a ByRef parameter (for some reason). When you use ByRef, any changes made to the parameter inside the method must be reflected to the variable that was passed to the method. That happens by reassigning the value to the original variable.

In your case, it works like this:

  • The Remove() method is called and a variable (Cases(caseIndex)) is passed to it.
  • Some work is done inside the Remove() method which might, or might not include changing the value of the parameter BeltCase.
  • The value of the parameter BeltCase gets reassigned to the variable that was originally passed to the method (which is Cases(caseIndex)).
  • As a result of the above step, the setter of the BeltCase property gets called with Index = 2 which raises the out of range exception because Cases(2) doesn't exist (was removed).

To confirm, you can see this problem go away when you replace this line:

Cases.Remove(Cases(caseIndex))

..with:

Dim myCase As CaseClass = Cases(caseIndex)
Cases.Remove(myCase)

That way, you create a new variable which refers to the same CaseClass object and most importantly avoid calling the setter of your Collection.BeltClase property.

However, a better solution would be to not use ByRef in the first place since you don't seem to need it in this situation. So, simply use Public Sub Remove(ByVal BeltCase As CaseClass) instead.

Check this question for more about ByVal and ByRef with objects.

One last thing, please don't call your class Collection because it can be very confusing to anyone looking at your project.