Was hier passiert ist, dass WithEvents
ist eine Funktion, die das .NET Framework selbst nicht nativ unterstützt. VB.NET implementiert es zusätzlich zu .NET. Das Feature ist da, weil es auch von VB6 bereitgestellt wurde. Die Art, wie das Feature in VB6 implementiert wurde, ist jedoch aufgrund eines grundlegenden Unterschieds in den Ereignismodellen zwischen COM und .NET sehr unterschiedlich.
Ich werde nicht gehen, wie VB6 die Funktion implementiert; das ist nicht wirklich relevant. Wichtig ist, wie Ereignisse mit .NET funktionieren. Grundsätzlich müssen Ereignisse mit .NET explizit angehängt und abgehakt werden. Wenn Ereignisse definiert werden, gibt es viele Parallelen zur Definition von Eigenschaften. Insbesondere gibt es eine Methode, die einem Ereignis einen Handler hinzufügt, und eine Methode, die einen Handler entfernt, ähnlich der Symmetrie zwischen den "set" - und "get" -Methoden einer Eigenschaft.
Der Grund dafür, dass Ereignisse solche Methoden verwenden, besteht darin, die Liste angehängter Handler von externen Anrufern zu verbergen. Wenn Code außerhalb einer Klasse Zugriff auf die vollständige Liste der angehängten Handler hätte, wäre es möglich, dass er sich damit befasst, was eine sehr schlechte Programmierpraxis wäre, was möglicherweise zu einem sehr verwirrenden Verhalten führen würde.
VB.NET macht direkte Aufrufe dieser Ereignis "hinzufügen" und "entfernen" -Methoden über die AddHandler
und RemoveHandler
Betreiber. In C# wird die gleiche zugrunde liegende Operation mit den Operatoren +=
und -=
ausgedrückt, wobei das linke Argument eine Ereignismemberreferenz ist.
Was WithEvents
gibt Ihnen ist syntaktischer Zucker, der die AddHandler
und RemoveHandler
Anrufe versteckt. Was wichtig ist zu erkennen ist, dass die Anrufe immer noch da sind, sie sind nur implizit.
Also, wenn Sie Code wie folgt schreiben:
Private WithEvents _obj As ClassWithEvents
Private Sub _obj_GronkulatedEvent() Handles _obj.GronkulatedEvent
...
End Sub
..you VB fragen.NET, um sicherzustellen, dass welches Objekt auch immer _obj
zugewiesen wird (unter Berücksichtigung, dass Sie diese Objektreferenz jederzeit ändern können), sollte das Ereignis GronkulatedEvent
von diesem Sub
behandelt werden. Wenn Sie die Referenz ändern, sollte das alte Objekt GronkulatedEvent
sofort entfernt werden und das neue Objekt GronkulatedEvent
angehängt werden.
VB.NET implementiert dies, indem Sie Ihr Feld in eine Eigenschaft verwandeln. Das Hinzufügen von WithEvents
bedeutet, dass das Feld _obj
(oder in Ihrem Fall _item
) nicht eigentlich ein Feld ist. Ein Geheimnis dahinter liegende Feld erzeugt wird, und dann wird _item
eine Eigenschaft, deren Implementierung sieht wie folgt aus:
Private __item As ItemViewModel ' Notice this, the actual field, has two underscores
Private Property _item As ItemViewModel
<CompilerGenerated>
Get
Return __item
End Get
<CompilerGenerated, MethodImpl(Synchronized)>
Set(value As ItemViewModel)
Dim previousValue As ItemViewModel = __item
If previousValue IsNot Nothing Then
RemoveHandler previousValue.GronkulatedEvent, AddressOf _item_GronkulatedEvent
End If
__item = value
If value IsNot Nothing Then
AddHandler value.GronkulatedEvent, AddressOf _item_GronkulatedEvent
End If
End Set
End Property
Also, warum dies die „Verzögerung“ führen, dass Sie sehen? Nun, Sie können eine Eigenschaft "ByRef" nicht übergeben. Um etwas "ByRef" zu übergeben, müssen Sie dessen Speicheradresse kennen, aber eine Eigenschaft verbirgt die Speicheradresse hinter den Methoden "get" und "set". In einer Sprache wie C# würden Sie einfach einen Kompilierungsfehler erhalten: Eine Eigenschaft ist kein L-Wert, daher können Sie keinen Verweis darauf übergeben. VB.NET ist jedoch fehlerverzeihender und schreibt zusätzlichen Code hinter die Kulissen, damit die Dinge für Sie funktionieren.
in Ihrem Code Sie übergeben, was wie ein Feld aussehen, das _item
Mitglied in SetProperty
, die ByRef
die Parameter nimmt, so kann er einen neuen Wert schreiben. Aber wegen WithEvents
ist das _item
Mitglied wirklich eine Eigenschaft. Also, was macht VB.NET? Es schafft eine temporäre lokale Variable für den Anruf zu SetProperty
, und ordnet sie dann nach dem Aufruf die Eigenschaft zurück:
Public Property Item As ItemViewModel
Get
Return _item ' This is actually a property returning another property -- two levels of properties wrapping the actual underlying field -- but VB.NET hides this from you
End Get
Set
' You wrote: SetProperty(_item, value)
' But the actual code emitted by the compiler is:
Dim temporaryLocal As ItemViewModel = _item ' Read from the property -- a call to its Get method
SetProperty(temporaryLocal, value) ' SetProperty gets the memory address of the local, so when it makes the assignment, it is actually writing to this local variable, not to the underlying property
_item = temporaryLocal ' Once SetProperty returns, this extra "glue" code passes the value back off to the property, calling its Set method
End Set
End Property
Also, weil WithEvents
Ihr Feld auf eine Eigenschaft umgewandelt, VB.NET die tatsächlichen aufzuschieben hatte Zuweisung an die Immobilie bis nach dem Anruf an SetProperty
zurückkehrt.
Hoffe das macht Sinn! :-)
Danke Jonathan! –
Follow-up: Es gibt ältere Probleme, die die Verwendung von IDisposable unerschwinglich machen. Da wir IDisposable nicht implementieren können und darauf angewiesen werden, dass Dispose() aufgerufen wird, um die verbleibenden Handles zu bereinigen, dachte man, dass die Verwendung von WithEvents dies umgehen und die Bereinigung für uns übernehmen würde. ** 1) ** Gibt WithEvents tatsächlich die Handles frei, wenn das Objekt nicht mehr verwendet wird, so dass es richtig Garbage Collection gesammelt wird? ** 2) ** Gibt es eine Alternative zu IDisposable, die es uns ermöglichen würde, AddHandler und RemoveHandler zu verwenden, ohne Griffe auszugeben, wenn das Objekt nicht mehr benötigt wird? –
Hmm, naja, nein, 'WithEvents' macht überhaupt nichts in Bezug auf die Entsorgung von Objekten, weil es keine Verbindung zur Lebensdauer des Objekts hat. Es gibt nur einen Weg, um sicherzustellen, dass Objekte, die entsorgt werden müssen, tatsächlich entsorgt werden, und zwar die "Dispose" -Methode. Wenn Sie das Objekt während eines bestimmten Codebereichs verwenden, können Sie dafür sorgen, dass dies automatisch mit einer 'Using'-Anweisung geschieht. –