2 Stimmen

Was ist diese Liste von Objekten (arrayLists), die in VB.Net gelöscht wird?

Entschuldigen Sie, dass ich eine so grundlegende Frage zum Scoping stelle, aber ich verstehe offensichtlich etwas sehr Grundlegendes zum Scoping nicht. Ich habe eine sehr einfache Klasse:

Public Class testListClass
    ' This just contains a single list that is set by a property or the constructor
    Private classArrayList As New ArrayList()

    Public Sub New(ByVal theList As ArrayList)
        classArrayList = theList
    End Sub
End Class

Dann habe ich einen Codeblock, der diese instanziiert, wenn ich eine Taste drücken, die ein neues testListClass-Objekt mit drei Werten (1, 2, 3).

Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button3.Click
    ' Lets see if changing the arrayList results in all of the testListClass items being changed
    Dim theList As New List(Of testListClass)
    Dim localArrayList As New ArrayList()

    localArrayList.Add(1)
    localArrayList.Add(2)
    localArrayList.Add(3)

    theList.Add(New testListClass(localArrayList))

    ' This results in theList.classArrayList being cleared.  Why since the parameter
    ' to the constructor is passed by value?
    localArrayList.Clear()   

    localArrayList.Add(10)
    localArrayList.Add(20)

    theList.Add(New testListClass(localArrayList))

End Sub

Nach dem Aufruf "theList.Add(New testListClass(localArrayList))" enthält theList ein "testListClass"-Objekt, das drei Werte (1,2,3) enthält, so wie ich es erwarten würde. Das Folgende ist das, was ich nicht verstehe. Der nächste Aufruf ist:

localArrayList.Clear()  

Wenn ich hier im Debugger einen Haltepunkt setze und diese Zeile ausführe, sehe ich folgendes:

theList(0).classArrayList wurde jetzt gelöscht. Während sie vor clear() drei Werte (1,2,3) enthielt, wurde nach dem Aufruf zum Löschen des lokal definierten ArrayList der Inhalt von "theList(0)" nun gelöscht. Warum ist das so? Ich würde denken, da der New-Konstruktor-Parameter als Wert (ByVal) übergeben wird, dass eine lokale Änderung eines Containerwertes im aufrufenden Code keine Auswirkungen auf die Werte hat, die zuvor an eine andere Methode in einer anderen Klasse übergeben wurden. Welches offensichtliche Prinzip übersehe ich hier?

4voto

Eric Nicholson Punkte 3943

Sie wird als Wert übergeben, aber ArrayList ist (wie alle Klassen) ein Referenztyp. Also übergeben Sie die Referenz als Wert. .NET wird niemals Objekte für Sie kopieren, es sei denn, das Objekt selbst bietet eine Möglichkeit, dies zu tun.

In diesem Fall sollten Sie die Methode ArrayList.Clone() verwenden.

1voto

dreza Punkte 3565

Soweit ich weiß, bedeutet byval, dass die Zuweisung eines tatsächlichen neuen Objekts an den übergebenen byval-Parameter die Referenz in der aufrufenden Methode nicht aktualisieren würde. Sie können jedoch weiterhin auf die internen Werte des Objekts zugreifen und diese ändern, indem Sie auf seine Eigenschaften usw. zugreifen, da es sich immer noch um ein Objekt handelt.

d.h. wenn Sie dies tun würden

suppliedList = new List();

würde die Liste der aufrufenden Methode nicht zurückgesetzt.

suppliedList.Clear()

würde es.

1voto

Joel Coehoorn Punkte 377088

ByVal in .Net bedeutet nicht das, was Sie denken, dass es tut.

ArrayList ist eine Referenztyp . Wenn Sie einen Referenztyp ByVal übergeben, wird der Wert der Referenz selbst an die Funktion übergeben. Die Variable in der Funktion und die Variable von der Aufrufsite verweisen immer noch auf dasselbe Objekt im Speicher, so dass der Aufruf von .Clear() von der Aufrufsite das Objekt, das Sie zu Ihrer testList hinzugefügt haben, löscht.

Der Unterschied zwischen dieser Methode und der Übergabe des Objekts als ByRef besteht darin, was passiert, wenn Sie innerhalb der Funktion eine Zuweisung für das Objekt verwenden. Wenn Sie ByRef übergeben, wirken sich Zuweisungen, die innerhalb einer Funktion direkt an die Variable vorgenommen werden, auch auf eine Aufrufstelle aus. Wenn Sie ByVal übergeben, wirken sich diese Zuweisungen nicht auf die Aufrufsstelle aus.

Wenn Sie versuchen, das Programm zum Klonen Ihrer Liste zu zwingen, gibt es dafür keine eingebaute Unterstützung. Sie können es mit Werttypen vortäuschen, indem Sie die .ToList() Erweiterungsmethode, aber das ist nur ein Nebeneffekt der Methode, und für Referenztypen haben Sie eine Liste der dieselbe Objekte. Für andere Typen können Sie oft die Serialisierungsfunktionen von .Net verwenden, um Objekte zu klonen. In den meisten Fällen ist es jedoch am einfachsten und zuverlässigsten, dies von Hand zu tun.

0voto

Jon Egerton Punkte 38465

Das Problem ist, dass "localArrayList" ein Zeiger auf die Liste ist, wie sie im Speicher gehalten wird. Wenn Sie andere Dinge mit dieser Liste tun, wie z.B. sie zu Klassen oder Listen hinzufügen, machen Sie einfach mehr Zeiger auf den gleichen Speicherplatz, so dass Sie localArrayList.Clear() löscht einfach die Instanz in diesem Speicherbereich für alle Zeiger.

Stattdessen sollten Sie Folgendes tun Dim localArrayList As New ArrayList() wieder anstelle der Zeile, in der Sie einen Löschvorgang durchführen. Dadurch wird eine neue Instanz der Liste gestartet und die alte in Ruhe gelassen.

Außerdem macht BYVal keinen großen Unterschied, da es sich bei dem übergebenen Objekt nicht um einen Werttyp (z. B. eine Ganzzahl), sondern um einen Verweistyp (d. h. ein Objekt) handelt - wie ich bereits sagte, übergeben Sie den Verweis auf die Liste, nicht die Liste.

Weitere Informationen finden Sie in der folgenden Frage: ByRef vs ByVal Klärung

0voto

Martin Punkte 1380

Indem Sie schreiben: classArrayList = theList

Sie angeben, dass von nun an (bis zur Änderung) classArrayList y DieListe befinden sich an denselben physischen Speicherplätzen. Ändert man einen, ändert man auch den anderen.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X