Benutzer mit den meisten Antworten
Speichern von Referenzen in VB.NET

Frage
-
Hallo alle zusammen,
ich würde gern in VB.NET unter Visual Studio 2013 eine Referenz auf ein, einem Konstruktor, übergebenes Argument eines Wertetyps zwischenspeichern um mit einer Methode der Klasse später mit dieser Referenz wieder auf das externe Argument zuzugreifen.
Es gäbe also beispielsweise irgendwo im Programm ein Datum (oder Boolean oder Integer oder ...), etwa so:
Public Property SomeDate As Date
und das würde an den Konstruktor einer Klasse (meinetwegen sClass, etwa so:
Public Sub New(ByRef sDate As Date)
)
übergeben werden, also so:
Dim something as sClass = New sClass(SomeDate)
Und die Klasse sClass hätte eine Routine, mit der das referenzierte Datum irgendwann einmal bearbeitet werden könnte/sollte, also beispielsweise so:
Public Class sClass ... Public Sub Something() sDate = Now() End Sub ... End Class
Dazu müsste die Referenz auf sDate natürlich zwischengespeichert werden. Das könnte zwar so aussehen
Public Class sClass ... Private tmpDate as Date ... Public Sub New(ByRef sDate as Date) tmpdate = sDate End Sub ... Public Sub Something() tmpDate = Now() End Sub ... End Class
funktioniert aber nicht, da Date ja kein Referenztyp ist.
Und wenn ich statt eines Typs Date eine extra dafür erstellte Klasse übergeben würde, hätte ich zwar mit der Klasse einen Referenztyp. Aber auch in dieser Klasse müsste ich ja die Referenz auf das Datum speichern, womit ich wieder das selbe Problem hätte.
Sehe ich den Wald vor lauter Bäumen nicht oder sollte es wirklich nicht gehen?
Oder hat jemand eine Idee???
Vielen Dank für Eure Tipps.
Gruß, Werner
WM
- Bearbeitet WerMil Freitag, 1. September 2017 06:39
Antworten
-
Hi Werner,
nachfolgend eine Konsolendemo zur Verdeutlichung:Module Module05 ''' <summary> ''' Demo-Umgebung ''' </summary> Sub Main() Try Dim c As New Demo ' Instanz der Klasse für die Demo c.Execute() ' Demo starten Catch ex As Exception Console.Write(ex.ToString) ' ggf. Fehler anzeigen End Try Console.Write("weiter mit Taste") ' Stopper für die Konsolenoberfläche Console.ReadKey() ' mit Tastenanschlag Programm beenden End Sub ''' <summary> ''' Klasse mit der Variablen ''' </summary> Class Demo ''' <summary> ''' private Feld, was von "außen" geändert werden soll ''' </summary> Private Field1 As Integer ''' <summary> ''' Demo ''' </summary> Friend Sub Execute() Field1 = 1 ' Wert lokal setzen ' "fremde" Klasse instanziieren mit Delegate auf Methode, die Änderung ausführt Dim c As New Demo2(AddressOf WertAendern) ' Kontrolle des alten Wertes Console.WriteLine($"alter Wert: {Field1}") ' in "fremden" Objekt etwas machen, was Änderungen am Original-Feld ausführt c.Methode1() ' Kontrolle des neuen Wertes Console.WriteLine($"neuer Wert: {Field1}") End Sub ''' <summary> ''' Methode, die über Delegate von "fremden" Objekt ausgeführt wird ''' </summary> ''' <param name="neuerWert">von "fremdem" Objekt übergebener Wert</param> Private Sub WertAendern(neuerWert As Integer) Field1 = neuerWert End Sub End Class ''' <summary> ''' Delegate-Deklaration der vom "frendem" Objekt aufgerufenen Methode ''' damit kann die CLR die Typsicherheit prüfen ''' </summary> ''' <param name="newValue">formaler Parameter für den Wert</param> Delegate Sub SetValue(newValue As Integer) ''' <summary> ''' Typ für das "fremde" Objekt ''' </summary> Class Demo2 ''' <summary> ''' privates Feld, in welchem die Rücksprungadresse (Delegate) gemerkt wird ''' </summary> Private del As SetValue ''' <summary> ''' Konstruktor, mit dem die Rücksprungadresse (Delegate) übergeben wird ''' </summary> ''' <param name="wertNeuSeten"></param> Public Sub New(wertNeuSeten As SetValue) del = wertNeuSeten ' die Rücksprungadresse (Delegate) merken End Sub ''' <summary> ''' Methode, die die Rücksprungadresse (Delegate) nutzt (ausführt) ''' und damit den Wert am Originalplatz ändert ''' </summary> Friend Sub Methode1() del.Invoke(555) End Sub End Class End Module
--
Viele Grüsse
Peter Fleischer (ehem. MVP)
Meine Homepage mit Tipps und Tricks
- Als Antwort vorgeschlagen Peter Fleischer Freitag, 1. September 2017 10:18
- Bearbeitet Peter Fleischer Freitag, 1. September 2017 10:20
- Als Antwort markiert Dimitar DenkovMicrosoft contingent staff, Administrator Mittwoch, 13. September 2017 13:53
-
Hi Werner,
mit anonymer Methode (Lambda) könnte das so aussehen:Module Module05 ''' <summary> ''' Demo-Umgebung ''' </summary> Sub Main() Try Dim c As New Demo ' Instanz der Klasse für die Demo c.Execute() ' Demo starten Catch ex As Exception Console.Write(ex.ToString) ' ggf. Fehler anzeigen End Try Console.Write("weiter mit Taste") ' Stopper für die Konsolenoberfläche Console.ReadKey() ' mit Tastenanschlag Programm beenden End Sub ''' <summary> ''' Klasse mit der Variablen ''' </summary> Class Demo ''' <summary> ''' private Feld, was von "außen" geändert werden soll ''' </summary> Private Field1 As Integer ''' <summary> ''' Demo ''' </summary> Friend Sub Execute() Field1 = 1 ' Wert lokal setzen ' "fremde" Klasse instanziieren mit Lambda, der Änderung ausführt Dim c As New Demo2(Sub(neuerWert As Integer) Field1 = neuerWert End Sub) ' Kontrolle des alten Wertes Console.WriteLine($"alter Wert: {Field1}") ' in "fremden" Objekt etwas machen, was Änderungen am Original-Feld ausführt c.Methode1() ' Kontrolle des neuen Wertes Console.WriteLine($"neuer Wert: {Field1}") End Sub End Class ''' <summary> ''' Delegate-Deklaration der vom "frendem" Objekt aufgerufenen Methode ''' damit kann die CLR die Typsicherheit prüfen ''' </summary> ''' <param name="newValue">formaler Parameter für den Wert</param> Delegate Sub SetValue(newValue As Integer) ''' <summary> ''' Typ für das "fremde" Objekt ''' </summary> Class Demo2 ''' <summary> ''' privates Feld, in welchem die Rücksprungadresse (Delegate) gemerkt wird ''' </summary> Private del As SetValue ''' <summary> ''' Konstruktor, mit dem die Rücksprungadresse (Delegate) übergen wird ''' </summary> ''' <param name="wertNeuSeten"></param> Public Sub New(wertNeuSeten As SetValue) del = wertNeuSeten ' die Rücksprungadresse (Delegate) merken End Sub ''' <summary> ''' Methode, die die Rücksprungadresse (Delegate) nutzt (ausführt) ''' und damit den Wert am Originalplatz ändert ''' </summary> Friend Sub Methode1() del.Invoke(555) End Sub End Class End Module
--
Viele Grüsse
Peter Fleischer (ehem. MVP)
Meine Homepage mit Tipps und Tricks- Als Antwort vorgeschlagen Peter Fleischer Freitag, 1. September 2017 20:21
- Als Antwort markiert Dimitar DenkovMicrosoft contingent staff, Administrator Mittwoch, 13. September 2017 13:56
Alle Antworten
-
Hi Werner,
im Konstruktor der Klasse willst Du Dir die Referenz auf den Speicherplatz des formalen Parameters merken. Dazu musst Du eine Zuweisung nutzen. Wegen der durchgängigen Typprüfung und Typsicherheit in .NET wird mit der Zuweisung immer der Inhalt das formalen Parameters kopiert. Wenn ByVal genutzt wird, dann wird bereits mit Methodenaufruf eine Kopie erstellt. Wenn ByRef genutzt wird, wird erst mit der Zuweisung im Konstruktor der Variableninhalt kopiert. Es wird immer der Inhalt der Variablen, die entsprechend Signatur übergeben wurde, kopiert. Bei Wertetypen ist das der Wert selbst, bei Referenztypen ist das eine Kopie der Referenz.Wenn eine aufgerufene Methode den Inhalt einer Variablen ändern soll, deren Referenz sich an anderer Stelle (Konstruktor oder andere Methode) gemerkt werden soll, dann geht das nur mittels Delegates. Es wird also nicht die Variable übergeben, sondern ein Delegate einer Methode. Die dem Delegate zugewiesene Methode befindet sich im Scope der Variablen und führt die Änderungen am Originalplatz aus. Die dem Delegate zugewiesene Methode wird dann als formaler Parameter dem "fremden" Objekt übergeben (z.B. im Konstruktor). Aufgerufen wird der Delegate dann in irgendeiner Methode in diesem "fremden" Objekt.
Ich hoffe, ich habe das etwas verständlich dargestellt.
--
Viele Grüsse
Peter Fleischer (ehem. MVP)
Meine Homepage mit Tipps und Tricks -
Hallo Peter und danke für Deine schnelle Antwort.
Ich hatte schon geahnt, dass es mit Delegates irgendwie gehen würde.
Bisher habe ich aber einen Ansatz weder selbst dazu gefunden noch Deiner Antwort entnehmen können.
Ich konnte leider auch aus "... befindet sich im Scope der Variablen..." keinen Ansatz ableiten.
Könntest Du ein kurzes Beispiel anführen?
Danke vorab und Gruß, Werner
WM
-
Hi Werner,
nachfolgend eine Konsolendemo zur Verdeutlichung:Module Module05 ''' <summary> ''' Demo-Umgebung ''' </summary> Sub Main() Try Dim c As New Demo ' Instanz der Klasse für die Demo c.Execute() ' Demo starten Catch ex As Exception Console.Write(ex.ToString) ' ggf. Fehler anzeigen End Try Console.Write("weiter mit Taste") ' Stopper für die Konsolenoberfläche Console.ReadKey() ' mit Tastenanschlag Programm beenden End Sub ''' <summary> ''' Klasse mit der Variablen ''' </summary> Class Demo ''' <summary> ''' private Feld, was von "außen" geändert werden soll ''' </summary> Private Field1 As Integer ''' <summary> ''' Demo ''' </summary> Friend Sub Execute() Field1 = 1 ' Wert lokal setzen ' "fremde" Klasse instanziieren mit Delegate auf Methode, die Änderung ausführt Dim c As New Demo2(AddressOf WertAendern) ' Kontrolle des alten Wertes Console.WriteLine($"alter Wert: {Field1}") ' in "fremden" Objekt etwas machen, was Änderungen am Original-Feld ausführt c.Methode1() ' Kontrolle des neuen Wertes Console.WriteLine($"neuer Wert: {Field1}") End Sub ''' <summary> ''' Methode, die über Delegate von "fremden" Objekt ausgeführt wird ''' </summary> ''' <param name="neuerWert">von "fremdem" Objekt übergebener Wert</param> Private Sub WertAendern(neuerWert As Integer) Field1 = neuerWert End Sub End Class ''' <summary> ''' Delegate-Deklaration der vom "frendem" Objekt aufgerufenen Methode ''' damit kann die CLR die Typsicherheit prüfen ''' </summary> ''' <param name="newValue">formaler Parameter für den Wert</param> Delegate Sub SetValue(newValue As Integer) ''' <summary> ''' Typ für das "fremde" Objekt ''' </summary> Class Demo2 ''' <summary> ''' privates Feld, in welchem die Rücksprungadresse (Delegate) gemerkt wird ''' </summary> Private del As SetValue ''' <summary> ''' Konstruktor, mit dem die Rücksprungadresse (Delegate) übergeben wird ''' </summary> ''' <param name="wertNeuSeten"></param> Public Sub New(wertNeuSeten As SetValue) del = wertNeuSeten ' die Rücksprungadresse (Delegate) merken End Sub ''' <summary> ''' Methode, die die Rücksprungadresse (Delegate) nutzt (ausführt) ''' und damit den Wert am Originalplatz ändert ''' </summary> Friend Sub Methode1() del.Invoke(555) End Sub End Class End Module
--
Viele Grüsse
Peter Fleischer (ehem. MVP)
Meine Homepage mit Tipps und Tricks
- Als Antwort vorgeschlagen Peter Fleischer Freitag, 1. September 2017 10:18
- Bearbeitet Peter Fleischer Freitag, 1. September 2017 10:20
- Als Antwort markiert Dimitar DenkovMicrosoft contingent staff, Administrator Mittwoch, 13. September 2017 13:53
-
Hallo Peter und allerbesten Dank.
Das klappt!
Ich habe wieder einmal etwas gelernt.
Aber ich würde gern noch einen oben drauf geben und zwar nicht
AddressOf WertAendern
sondern statt dessen einen Lambda-Ausdruck, zunächst für WertAendern, eigentlich aber für irgendeine Routine übergeben.
Aber auch damit tue ich mich schwer. Es will mir nicht gelingen. Das Nicht-Gelingen fängt bereits beim Verständnis an. Hast Du dazu noch eine Idee?
Gruß, Werner
WM
-
Hi Werner,
mit anonymer Methode (Lambda) könnte das so aussehen:Module Module05 ''' <summary> ''' Demo-Umgebung ''' </summary> Sub Main() Try Dim c As New Demo ' Instanz der Klasse für die Demo c.Execute() ' Demo starten Catch ex As Exception Console.Write(ex.ToString) ' ggf. Fehler anzeigen End Try Console.Write("weiter mit Taste") ' Stopper für die Konsolenoberfläche Console.ReadKey() ' mit Tastenanschlag Programm beenden End Sub ''' <summary> ''' Klasse mit der Variablen ''' </summary> Class Demo ''' <summary> ''' private Feld, was von "außen" geändert werden soll ''' </summary> Private Field1 As Integer ''' <summary> ''' Demo ''' </summary> Friend Sub Execute() Field1 = 1 ' Wert lokal setzen ' "fremde" Klasse instanziieren mit Lambda, der Änderung ausführt Dim c As New Demo2(Sub(neuerWert As Integer) Field1 = neuerWert End Sub) ' Kontrolle des alten Wertes Console.WriteLine($"alter Wert: {Field1}") ' in "fremden" Objekt etwas machen, was Änderungen am Original-Feld ausführt c.Methode1() ' Kontrolle des neuen Wertes Console.WriteLine($"neuer Wert: {Field1}") End Sub End Class ''' <summary> ''' Delegate-Deklaration der vom "frendem" Objekt aufgerufenen Methode ''' damit kann die CLR die Typsicherheit prüfen ''' </summary> ''' <param name="newValue">formaler Parameter für den Wert</param> Delegate Sub SetValue(newValue As Integer) ''' <summary> ''' Typ für das "fremde" Objekt ''' </summary> Class Demo2 ''' <summary> ''' privates Feld, in welchem die Rücksprungadresse (Delegate) gemerkt wird ''' </summary> Private del As SetValue ''' <summary> ''' Konstruktor, mit dem die Rücksprungadresse (Delegate) übergen wird ''' </summary> ''' <param name="wertNeuSeten"></param> Public Sub New(wertNeuSeten As SetValue) del = wertNeuSeten ' die Rücksprungadresse (Delegate) merken End Sub ''' <summary> ''' Methode, die die Rücksprungadresse (Delegate) nutzt (ausführt) ''' und damit den Wert am Originalplatz ändert ''' </summary> Friend Sub Methode1() del.Invoke(555) End Sub End Class End Module
--
Viele Grüsse
Peter Fleischer (ehem. MVP)
Meine Homepage mit Tipps und Tricks- Als Antwort vorgeschlagen Peter Fleischer Freitag, 1. September 2017 20:21
- Als Antwort markiert Dimitar DenkovMicrosoft contingent staff, Administrator Mittwoch, 13. September 2017 13:56
-
Hallo Peter und nochmals herzlichen Dank.
Es hat alle toll geklappt!!
Und ich weiß nun auch, warum ich mich mit dem Lambda-Ausdruck so schwer getan hatte: Ich habe alles in einer Zeile gehabt und da ich dies nicht mit End Sub abgeschlossen hatte, hat VS auch nicht gemeckert. Es geht aber tatsächlich nur mit einem mehrzeiligen Lambda-Ausdruck und dessen Abschluss mit End Sub!
Tolle schnelle Hilfe!!!
Ein schönes Wochenende und Gruß, Werner
WM