none
Speichern von Referenzen in VB.NET RRS feed

  • 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
    Freitag, 1. September 2017 06:38

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



    Freitag, 1. September 2017 09:12
  • 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

    Freitag, 1. September 2017 12:32

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

    Freitag, 1. September 2017 08:05
  • 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

    Freitag, 1. September 2017 08:30
  • 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



    Freitag, 1. September 2017 09:12
  • 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

    Freitag, 1. September 2017 11:58
  • 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

    Freitag, 1. September 2017 12:32
  • 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

    Freitag, 1. September 2017 15:08