Laden...

[erledigt] ReadOnly List<T>: Nicht nur die Liste, sondern auch deren Elemente schreibschützen

Erstellt von ..Heinz.. vor 11 Jahren Letzter Beitrag vor 11 Jahren 2.889 Views
.
..Heinz.. Themenstarter:in
134 Beiträge seit 2009
vor 11 Jahren
[erledigt] ReadOnly List<T>: Nicht nur die Liste, sondern auch deren Elemente schreibschützen

Hi,

ich habe eine kleine Frage zur List<T>.

In der List<T> gibt es ja die Funktion "AsReadOnly", mit der ich eine neue Collection bekomme, die kein Schreiben in die Collection ermöglicht.

Nun mein Problem und meine Frage.

Wenn ich als T eine eigene Klasse hinterlege, die z.B, zwei Properties hat. Und ich aus der ReadOnlyCollection ein Item auslese und eines der beiden Properties verändere. Wird dies gemacht.

Gibt es eine Möglichkeit, den Speicherbereich auch ReadOnly zu machen, so dass die komplette Liste, inkl. der Items schreibgeschützt ist?

Gruß

..Heinz..

S
417 Beiträge seit 2008
vor 11 Jahren

Hallo,

ReadOnly bezieht sich nur auf die Datenstruktur, sprich List<T>, und nicht auf die enthaltenen Objekte.
Wenn diese auch nicht verändert werden dürfen, musst du das selbst implementieren.
Aber was ist der dahinter liegende Anwendungsfall? Evtl. gibt es ja eine andere (bessere) Lösung...

4.931 Beiträge seit 2008
vor 11 Jahren

Hallo Heinz,

lösen kann man das mit Hilfe eines Interfaces:


interface IMyData
{
   int PropA { get; }
   int PropB { get; }
}

Deine Datenklasse implementiert dann dieses Interface und du gibst dann einfach eine ReadOnly<IMyData>-Collection zurück.

.
..Heinz.. Themenstarter:in
134 Beiträge seit 2009
vor 11 Jahren

Hallo Sarc und Th69. Danke für eure schnellen Antworten.

@Sarc: Was ich vorhabe ist ein Dictionary, das global im Programm verfügbar ist. Man kann von überall (zum lesen und schreiben) auf diese Liste zugreifen. Ich habe jetzt implementiert, dass ich sagen kann, ob bestimmte Items gelöscht werden dürfen: Add(item, canDeleted). Auslesen geht immer. Nur kann man über list[x] das Item auslesen und es verändern (ggf. sogar leeren). Und genau das würde ich gerne verhindern.

@TH69: Danke für deine Antwort. Aber ich würde es gerne generisch machen, so dass ich Objekte anhängen kann, ohne dass ich ihre Struktur kenne.

F
10.010 Beiträge seit 2004
vor 11 Jahren

Ganz abgesehen davon das so eine Globale liste in 120% der fälle ein Verstoss gegen jegliche vernünftige Architektur ist, wäre das über ein generischen Proxy leicht möglich.
In MicroModels habe ich das mal in 10 Zeilen eingebaut.

.
..Heinz.. Themenstarter:in
134 Beiträge seit 2009
vor 11 Jahren

Hallo FZelle,

ich weiß, dass globale Variablen nicht gut sind. Es ist nur ein Test, der später verbessert wird 😉 Kannst du mir einen kleinen Anstoß geben, wie da am besten anfange?

F
10.010 Beiträge seit 2004
vor 11 Jahren

Es ist nur ein Test, der später verbessert wird 😉

Wer's glaubt.
In der IT ist nichts so beständig wie das Provisorium.

Schon das Du meinst es so benutzen zu wollen zeigt das Du keinen Architekturansatz hast sondern nur drauflos machst.

Evtl solltest Du mal statt nur Biblioteken wie Caliburn.Micro zu benutzen den Architekturansatz dahinter verstehen.
Also Sachen wie MVVM, DI/IOC usw.

Wozu meinst Du denn überhaupt sowas brauchen zu wollen?

.
..Heinz.. Themenstarter:in
134 Beiträge seit 2009
vor 11 Jahren

Hallo FZelle,

ich will dir jetzt nicht zu nahe treten, aber wieso willst du wissen, dass es keinen Architekturansatz gibt? Nur ein kleiner Auszug aus meinen Projekten: Ich programmiere mit WPF und WinForms in C#, VB.NET und VBA, weiß was MVVM ist und weiß, wie ich es benutzen kann. Ebenso verwende ich Caliburn.Micro für MVVM. Für IoC verwende ich z.B. Windsor Container. Diese Themen sind so weitreichend, dass es verständlich ist, wenn man Fragen dazu hat.

Entschuldige, wenn meine Frage ein Thema anspricht, das so nicht umgesetzt werden soll. Aber wissen zu wollen, wie man ein Problem lösen kann (es geht mir nicht darum, fertigen Code zu bekommen, ein Denkanstoß reicht vollkommen), dann eine einfache Lösung umzusetzen und daran rum zu basteln bis sie "groß" ist und passt, ist m.M.n. nichts verwerfliches. Wir wollen alle lernen, sonst wäre wir nicht hier. Und da sollte es erlaubt sein, Fragen stellen zu dürfen ohne dass einem die Qualifikation und das Verständnis für Programmierung und Softwareentwicklung abgesprochen wird.

Ich bin froh über alle Hilfestellungen, egal in welcher Form. Aber was ich nicht mag ist, wenn man Qualifikationen abgesprochen bekommt, nur weil man etwas anders löst als es andere für richtig empfinden.

Zurück zum Thema. Kannst du mir einen kleinen Denkanstoß geben? Vllt. auch in eine andere Richtung. Ich bin für alles offen, wenn ich etwas neues lernen kann.

F
10.010 Beiträge seit 2004
vor 11 Jahren

Noch einmal, was soll das werden das Du meinst es so lösen zu wollen.

.
..Heinz.. Themenstarter:in
134 Beiträge seit 2009
vor 11 Jahren

Ich suche nach einer Möglichkeit, eine Liste an einer zentralen Stelle zur Verfügung zu stellen, auf die man von allen Formularen (in WinForms) und von allen Windows (WPF) Zugriff hat. So dass z.B. Window1 einen Eintrag schreibt, der von Window2 gelesen, aber nicht verändert werden kann. Ich will darüber Daten verarbeiten, die zur Information des Benutzers dienen.

Ich weiß, wie man Daten von einem Formular an ein anderes (sicher und konform) übergibt. Nur interessiert es mich, wie man diese Lösung implementieren könnte.

2.078 Beiträge seit 2012
vor 11 Jahren

Also so, wie ich es jetzt verstanden habe, möchtest du eine Art Datenspeicher bieten, der beliebig erweitert werden kann, aber enthaltene Elemente dürfen nicht geändert werden.
Stimmt das soweit? Wenn nicht, dann nicht weiter lesen^^

Also ich würde das jetzt wie folgt lösen:

Und zwar würde ich mir eine Klasse schreiben, die alle Elemente private enthält.
Damit du auch welche hinzu fügen kannst, baust du noch eine Add-Methode, die bei einer Liste schlicht die Add-Methode der private-Liste enthält.

Und die Elemente der Liste rufst du mittels Indexer ab, oder wenn du die ganze Liste haben willst, kannst du sie dir auch einfach so über eine Methode geben lassen.

Beispiel:

    public class Klasse
    {
        // Und hier ist die eigentliche Liste, vor jedem Zugriff geschützt
        private List<int> PublicList;

        // Der Konstruktor. Kann ja auch ohne Parameter sein
        public Klasse(params int[] Elements)
        {
            this.PublicList = new List<int>();
            this.PublicList.AddRange(Elements);
        }

        // Die Indexer, der einzelne Elemente aus gibt
        public int this[int index]
        {
            get { return PublicList[index]; }
        }

        // Die Add-Methode, über die Elemente hinzu gefügt werden können
        public void AddElement(int Number)
        {
            PublicList.Add(Number);
        }
    }

Ist getestet, allerdings nur kurz nebenbei, ob ich einen Syntax-Fehler bekomme und den gibt es, wenn ich irgendwie etwas schreibe, das die Daten ändern würde^^
Ich kann da nun Elemente vom Typ int speichern, hinzu fügen und abrufen, allerdings nicht ändern. Auch nicht über eine Methode, da das über den einen kleinen Umweg verhindert wird.

Mein Beispiel ist jetzt nur ein Beispiel und vielleicht ein etwas komplizierter Umweg. Wie das dann bei dir aussieht, hab ich keine Ahnung, aber vielleicht hilft es dir ja ^^

Das kann dann auch generisch gelöst werden.
Da beliebig Daten hinzu gefügt werden können sollen, gehe ich einfach von einer Liste aus, wenn du keine Liste (oder ein Array) drin hast, geht es natürlich nicht.

Mit einem Dictionary geht es auch, dann setzt du den Typ vom Index im Indexer einfach auf den Typ des Dictionarys.

4.931 Beiträge seit 2008
vor 11 Jahren

Hallo Palladin007,

du hast hier nur die Klasse ReadOnly<int> nachimplementiert 😉
Ersetze einfach mal 'int' durch eine Klasse und schon kann der Anwender über den Indexer wiederum dessen Eigenschaften setzen (also Daten verändern).

Und dies will Heinz eben verhindern...

.
..Heinz.. Themenstarter:in
134 Beiträge seit 2009
vor 11 Jahren

Hallo Palladin007,

das hast du richtig verstanden 🙂 Vielen Dank für deine Antwort und das Beispiel.

Aber ich muss Th69 Recht geben. Der Zugriff auf die Properties eines eingefügten Objektes ist immernoch da...

Trotzdem danke für die Antwort 🙂

2.078 Beiträge seit 2012
vor 11 Jahren

Ok, die Antwort kommt unerwartet 😄
Und dabei war ich so stolz 😄

Ich probiere mal was rum, vielleicht finde ich ja ne Lösung^^
Deinen Vorschlag mit einem Interface probiere ich auch mal aus.

Wenn ich ne Lösung finde, sag ich Bescheid^^

PS:
Das packt mich jetzt, das Thema 😄
Ich knobel gerne^^

F
10.010 Beiträge seit 2004
vor 11 Jahren

@..Heinz..:
DI/IOC ist z.b. nicht nur dazu da irgendwelche Dienste oder Commands zu injecten, es kann und soll auch einfache Objekte injekten, Settings z.b. oder den aktuellen User.
Und wenn du das mit dem Proxy mal nachgelesen hättest, wäre das Readonly Objekt auch schon gegessen.

2.078 Beiträge seit 2012
vor 11 Jahren

Ok, ich geb auf -.-

Hab jetzt ne Menge Blödsinn versucht und zum Schluss sogar ausprobiert, ob sich mit Zeigern was anstellen lässt, aber das ist mindestens genauso Blödsinn, wie der andere Kram, den ich versucht hab.

Ich kann dir leider nicht sagen wie, aber eine spontane Idee von mir wäre doch die:
Es gibt einen Cache. Wenn dann der get-Block der Eigenschaft aufgerufen wird, werden an erster Stelle alle Daten in diesen Cache gelegt.
Und danach, wenn alles beendet ist, also auch nach dem return, wird ein Event ausgelöst, das diesen Cache wieder in den eigentlichen Datenspeicher entleert.

Aber da kann ich nicht helfen.

Was ich als letztes noch ausprobieren würde, wäre der Versuch, alle Daten über einen Stream zwischen zu speichern und aus diesem Stream wieder zu lesen. Nur kenne ich keine Möglichkeit, die aus jedem Referenz-Typ einen Stream machen kann und dabei auch noch zufriedenstellende Zeiten braucht. XML-Serialisierung funktioniert ja nicht immer und ist, bei großen Datenmengen doch etwas zu langsam, für meinen Geschmack.

So, eine menge komische Ideen, vielleicht helfen sie euch beim lösen des Problems, ich glaub jedenfalls nicht, dass man da noch viel weiter denken braucht.

Vielleicht hab ich noch irgendwann einen Gedankenblitz, dann sag ich Bescheid, aber bis dahin klinke ich mich aus und lass die Profis weiter grübeln^^

F
10.010 Beiträge seit 2004
vor 11 Jahren

Auf Basis eines der vielen Proxy Frameworks ist sowas recht schnell gemacht.

Ich habe mal LinFu Dynamic Proxy benutzt und in 5 Minuten das hier erzeugt.

49.485 Beiträge seit 2005
vor 11 Jahren

Hallo ..Heinz..,

Wir wollen alle lernen, sonst wäre wir nicht hier.

richtig, und deshalb solltest du dich auch bitte nicht angegriffen fühlen, wenn dich jemand darauf hinweist, dass es bessere Ansätze oder Architekturen gibt, als die die dir anfangs vorschwebt sind.

Schon für ein ganz normales Dictionary<> aus dem .NET-Framework gilt

Zitat von: Dictionary<TKey
Wenn ein Objekt im Dictionary<TKey, TValue> als Schlüssel verwendet wird, darf es nicht auf eine Weise geändert werden, die sich auf seinen Hashwert auswirkt.

Da wird auch nicht mühsam versucht, eine Änderung mit technischen Mitteln zu verhindern. Es steht einfach in der Doku, fertig. Wers trotzdem tut, fliegt auf die Nase.

Dass in deinem Fall der Wunsch nach einem technischen Schutz aufgetaucht ist, liegt vermutlich wirklich daran, dass das Dictionary global verwendet werden soll und somit nicht nur die Auswirkungen einer (unerlaubten) Änderung entsprechend groß wäre, sondern gleichzeitig die Ursache bzw. der Verursacher (kann ja prinzipiell jedes Fenster und jede Codestelle gewesen sein) schwerer zu finden sind. Unerlaubte Änderungen zu verhindern, ist daher nur ein herumdoktern an den Symptomen. Damit wird der Einwand von FZelle, den ich grundsätzlich teile, vielleicht etwas verständlicher.

herbivore

U
1.688 Beiträge seit 2007
vor 11 Jahren

Hallo,

Aber ich würde es gerne generisch machen, so dass ich Objekte anhängen kann, ohne dass ich ihre Struktur kenne.

ganz ohne eine "Konvention" wird es nicht gehen. Auch List<>.AsReadOnly gibt ja nicht List<> zurück, sondern eine ReadOnlyCollection<>.

Eine Konvention könnte sein, für die Daten nur Werttypen (struct) zu verwenden.
Eine andere Konvention wären (in Analogie zu Strings) "immutable" Klassen. Dazu gibt es diverse Blog-Beiträge (Vor- und Nachteile, Zweck, Beispiele), die unter Immutability in C# verlinkt sind.

.
..Heinz.. Themenstarter:in
134 Beiträge seit 2009
vor 11 Jahren

Hi,

das Problem ist gelöst. =)

Danke an alle die mir geholfen haben.

Ich habe den Ansatz von FZelle etwas abgeändert umgesetzt und habe mir jetzt ein ReadOnlydictionary gebaut, in dem ich für jeden Eintrag (Key) die Eigenschaft IsReadOnly gesondert auf true oder false (je nachdem, ob der Wert (Value) geschützt sein soll, oder nicht.

@herbivore: Es geht mir nicht darum, dass ich nicht wissen will, wie es auch anders (vllt. besser) geht. Aber wenn ich ein Problem habe, das ich gerne lösen würde, interessiert es mich in erster Linie was ich falsch gemacht habe, bzw. was ich vergessen habe.

Nochmal Danke an alle =)