Eine ReadOnly Eigenschaft festlegen

  • VB.NET
  • .NET (FX) 4.5–4.8

Es gibt 8 Antworten in diesem Thema. Der letzte Beitrag () ist von TheCokeSideOf.

    Eine ReadOnly Eigenschaft festlegen

    Hallo!

    Ich habe zwei Klassen erstellt, eine ist eine Datenverarbeitungsstruktur genannt Verarbeitung und eine Datenspeicherstruktur genannt Speicher. Die Speicher werden der Verarbeitung zugeordnet und haben eine Eigenschaft 'owner', die den Namen der übergeordneten Verarbeitung trägt. Um die Speicher besser zu organisieren, habe ich eine SpeicherCollection-Klasse hinzugefügt, die von Collection(T) erbt.
    Das ist alles sehr ähnlich zu einem Datagridview: die Verarbeitung-Klasse ist wie das Datagridview, die Speicher wie eine DatagridviewRow, die .owner Eigenschaft ist wie deren .Datagridview Eigenschaft und die SpeicherCollection wie die DatagridviewRowCollection.

    Um alles möglichst gut abzukapseln, möchte ich nun die .owner Eigenschaft ReadOnly machen und erst dann festlegen, wenn ich SpeicherCollection.Add(Speicher) aufrufe. Das geht aber natürlich nicht wenn die Eigenschaft ReadOnly ist. Wenn man in einem Beispiel DatagridviewExample.Rows.Add(rowExample) aufruft, wird der rowExample.Datagridview Eigenschaft jedoch automatisch das Objekt DatagridviewExample zugeordnet, obwohl sie eigentlich ReadOnly ist.
    Weiß jemand wie das trotz ReadOnly möglich ist und wie ich das genauso machen kann?

    Vielen Dank!
    @TheCokeSideOf ReadOnly-Variablen kannst Du ausschließlich im Konstruktor dieser Klasse belegen.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Hallo

    Du kannst die Eigenschaft nicht von aussen festlegen da Read-Only. Du kannst aber den Wert im Kontruktor mitgeben. So kannst du den Wert setzen und er kann nachträglich nicht mehr verändert werden.

    Zumindest wenn ich dich nicht falsch verstehe.
    PS: Besser ist immer so ein kleines Beispiel zu machen, dann kennt sich jeder aus.

    Grüße
    Sascha
    If _work = worktype.hard Then Me.Drink(Coffee)
    Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

    ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

    Man könnte jedoch weiterhin, wenn natürlich gewünscht, den Wert jederzeit über ein Sub oder Function ändern. Im Sub/Function dann auf das Feld zugreifen. Im Fall von Autoproperties dann über _"Name der Property". Jedoch sind die Felder Autoproperties immer private, deswegen aufpassen beim Vererben.

    Inwieweit das Sinn macht Sei mal dahin gestellt :)
    Das gleiche Problem hast du auch bei direktem Zugriff auf readonly Collections. Mann kann zwar keinen Wert direkt zuweisen, aber immer noch die Collection selbst verändern.

    Da hilft dann nur die Collection privat lassen und nur die Methoden über Properties nach draußen übergeben, die benötigt sind und Einschränkungen im Setter der Property verarbeiten
    Danke für die Antworten!

    Hier noch etwas Code zu meiner Fragestellung:

    VB.NET-Quellcode

    1. Dim testrow As New DataGridViewRow
    2. 'Zu diesem Zeitpunkt ist die ReadOnly-Eigenschaft testrow.DataGridView noch Nothing
    3. DataGridView1.Columns.Add("test", "test")
    4. DataGridView1.Rows.Add(testrow)
    5. 'Nun ist testrow.DataGridView = DataGridView1

    Genau das würde ich auch gerne für meine Speicher.owner Eigenschaft erreichen: Eine eigentlich ReadOnly-Eigenschaft, die sich nur dann ändern lässt, wenn ein Speicher einem Verarbeitung-Klasse Objekt durch die SpeicherCollection zugeordnet wird. Also:

    VB.NET-Quellcode

    1. Dim testSpeicher As New Speicher
    2. 'Die ReadOnly-Eigenschaft testSpeicher.owner ist Nothing
    3. Verarbeitung1.SpeicherCollection.Add(testSpeicher)
    4. 'testSpeicher.owner ist nun = Verarbeitung1

    Bei genauerer Betrachtung der DataGridViewRow Dokumentation hier ist mir die OnDataGridViewChanged()-Methode aufgefallen. Ich nehme an, dass diese die .DataGridView-Eigenschaft ändert, wenn sich das DataGridView verändert. Wie die private Methode durch Rows.Add aufgerufen wird, ist mir allerdings auch nach langem Rumklicken schleierhaft.
    Weiß jemand wie das bei DataGridViewRows funktioniert, oder wie ich einen ähnlichen Mechanismus in meine Klasse integrieren könnte?
    also du möchtest, dass dem Auflistungs-Element sein Owner bekannt ist - readonly natürlich. Die Konstruktor-Lösung scheidet dabei aus, weil zum Erstell-Zeitpunkt des Elements ist sein Owner ja noch nicht bekannt.
    Ich würde hier ein Friend Interface einsetzen, was die Änderung der Element.Owner-Property erlaubt.
    Inne Owner.Add(element) - Methode castet der Owner das Element auf dieses Interface, und erhält dadurch Schreibrechte auf die Property (genauer: auf das Backing-Field hinter der Property).
    Der normale User, der iwas mit dem Element tut, weiß ja meist garnet, dasses das Interface ühaupt gibt (kann es aber natürlich im Objectbrowser einsehen).
    Friend bewirkt darüberhinaus, dass ausserhalb der Assembly dieses Interface garnet verfügbar ist.

    Also das ist keine wirklich sichere Kapselung, sondern nur ein Verbergen von Funktionalität. Kann man inne Doku-Kommentation des Interfaces auch vermerken (im ObjectBrowser sichtbar), dass es nur für interne Verarbeitung vorgesehen ist.
    Wenn ein Bösling trotzdem damit rumwerkelt - und ggfs. Nase fällt - selber schuld.



    Totale Kapselung gibts ja garnet in .Net - man kann ungefähr alles aufbohren, indem man mit Reflection das .Net-Typ-System umgeht. Wie gesagt: Wer solche Hacks einsetzt, sollte genau wissen, wasser tut.



    Übrigens mit Controls und ControlCollection ist ein ähnliches Problem anders gelöst:
    Bei Control ist das ParentControl nicht readonly - stattdessen, wenn du einen neuen Parent angibst, wird das Element aus dem alten Parent entfernt und im neuen Parent zugefügt.
    Auch so kann man diesbezüglich Konsistenz erreichen.

    Dieser Beitrag wurde bereits 6 mal editiert, zuletzt von „ErfinderDesRades“ ()

    Ist für Dich vielleicht keine passende Lösung, aber für andere könnte das nützlich sein:
    Wenn Du z.B. eine Bibliothek programmierst, in der diese Klassen drin sind, dann kannst Du Friend verwenden:

    VB.NET-Quellcode

    1. 'In Deiner Bibliothek
    2. Public Class Verarbeitung
    3. Private _SpeicherCollection As SpeicherCollection
    4. Public ReadOnly Property SpeicherCollection As SpeicherCollection
    5. Get
    6. Return _SpeicherCollection
    7. End Get
    8. End Property
    9. Public Sub New()
    10. _SpeicherCollection = New SpeicherCollection(Me)
    11. End Sub
    12. End Class
    13. Public Class SpeicherCollection
    14. Private Owner As Verarbeitung
    15. 'Hier *kann* es Sinn machen, den Konstruktor als "Friend" zu deklarieren. Dadurch können außerhalb dieser Bibliotheken keine Instanzen der SpeicherCollection erstellt werden.
    16. Public/Friend Sub New(NewOwner As Verarbeitung)
    17. Owner = NewOwner
    18. End Sub
    19. Public Sub Add(Item As Speicher)
    20. '*Schnipp*
    21. Item.owner = Me.Owner 'Kompiliert einwandfrei, weil man von hier aus auf den Setter zugreifen darf.
    22. End Sub
    23. End Class
    24. Public Class Speicher
    25. Private _owner As Verarbeitung
    26. Public Propety owner As Verarbeitung
    27. Get 'Der Getter ist Public (von der Zeile darüber "geerbt")
    28. Return _owner
    29. End Get
    30. Friend Set(value As Verarbeitung) 'Der Setter ist Friend, weil man das hier explizit nochmal dazugeschrieben hat.
    31. _owner = value
    32. End Set
    33. End Property
    34. End Class
    35. 'In einem anderen Projekt, das auf Deine Bibliothek verweist:
    36. Sub Test()
    37. Dim testSpeicher As New Speicher
    38. 'Die ReadOnly-Eigenschaft testSpeicher.owner ist Nothing
    39. testSpeicher.owner = testSpeicher 'Kompiliert nicht, da man von hier aus nicht auf den Setter der owner-Property zugreifen darf.
    40. Verarbeitung1.SpeicherCollection.Add(testSpeicher)
    41. 'testSpeicher.owner ist nun = Verarbeitung1
    42. End Sub

    (Warum ist owner eigentlich klein geschrieben? Properties schreibt man üblicherweise mit großen Anfangsbuchstaben.)
    "Luckily luh... luckily it wasn't poi-"
    -- Brady in Wonderland, 23. Februar 2015, 1:56
    Desktop Pinner | ApplicationSettings | OnUtils
    Vielen Dank, die Antworten helfen mir schon weiter!
    An Friend habe ich nicht gedacht, da ich selten Bibliotheken programmiere, aber das scheint mir eine sehr elegante Lösung zu sein.
    Das DataGridViewRow-Element scheint das zwar irgendwie anders zu lösen, aber eure Vorschläge finde ich da fast schöner.
    Ich werds also so einbauen und darauf achten, dass ich selbst nicht irgendwelche Schindluder treibe ;)

    Nochmals Danke!