Laden...

[GELÖST] Änderungen in Hashtable 1 werden auch für kopierte Hashtable 2 übernommen

Erstellt von Wichter vor 6 Jahren Letzter Beitrag vor 6 Jahren 1.481 Views
W
Wichter Themenstarter:in
6 Beiträge seit 2018
vor 6 Jahren
[GELÖST] Änderungen in Hashtable 1 werden auch für kopierte Hashtable 2 übernommen

Problembeschreibung: Ich arbeite in einer Form-Klasse mit zwei Hashtables, welche als Values selbstgeschriebene Objekte namens "KarteiKarte" enthalten. In der einen (NewCards) werden Änderungen gespeichert, die andere (BackUp) stellt eine Kopie der ersten dar, und sollte eigentlich nicht geändert werden damit der User mit dem Befehl "Zurücksetzen" alle Änderungen in der ersten wieder rückgängig machen kann. Problem: Änderungen in der ersten (NewCards) wirken werden auch für die zweite (BackUp) übernommen.

Die beiden Hashtables werden durch einen Parameter in der Funktion "Execute(Hashtable thisCards)" festgelegt:


public Hashtable Execute(Hashtable thisCards)
        {
//...
                foreach (DictionaryEntry myDicEntry in thisCards)
                {
                    BackUp.Add(myDicEntry.Key, myDicEntry.Value);
                }

                foreach (DictionaryEntry myDic1 in thisCards)
                {                    
                    NewCards.Add(myDic1.Key, myDic1.Value);
                }
//...

Später wird in derselben Formular-Klasse in einer anderen Funktion folgende Zuweisung an ein Value-Element der Klasse "NewCards" vorgenommen:


                KarteiKarte myCard1 = new KarteiKarte();

                myCard1 = NewCards[schluessel] as KarteiKarte;
                               
                myCard1.kartenPriorNew = 0;
                myCard1.Prioritaet = 0;
                //ab hier Problem: Änderung auch in "BackUp"

//...

Ich habe jetzt schon alle Möglich ausprobiert, u.a. auch diese Zuweisung:


   BackUp = thisCards;

Anzumerken wäre noch, dass die unerwünschten Änderungen selbst dann erhalten bleiben, wenn das Dialogfenster geschlossen wird. Beim nächsten Aufruf mit


                AlleKarten myAlleKarten = new AlleKarten();
                
                myCards2 = myAlleKarten.Execute(myCards2);

tauchen die Änderungen auch auf.

Ich hoffe ihr könnt mir helfen, vielen Dank!

Edit: GELÖST:

Ich habe meiner Klasse "KarteiKarte" eine Methode "Copy" hinzugfügt, die als Parameter eine zu kopierende KarteiKarte annimmt und eine kopierte KarteiKarte zurückgibt. Dort werden alle Eigenschaften von KarteiKarte per Hand übertragen:

public KarteiKarte Copy(KarteiKarte thisCard)
        {
            KarteiKarte myCard = new KarteiKarte();
            //...
            myCard.kartenPriorNew = thisCard.kartenPriorNew;
            myCard.Prioritaet = thisCard.Prioritaet;
            //...
            return myCard;
        }

Diese Funktion verwende ich anstatt direktes Kopieren wie oben beschrieben:

foreach (DictionaryEntry myDic1 in thisCards)
                {                    
                    KarteiKarte myCard = myDic1.Value as KarteiKarte;
                    myCard = myCard.Copy(myCard);
                    NewCards.Add(myCard.vorderseite, myCard);
//...                    
                    
                }

Wenn jemand eine elegantere Lösung, immer her damit.

Danke @all für die Hilfe!

1.040 Beiträge seit 2007
vor 6 Jahren

Wo erstellst du denn Kopien?

Wenn im DictionaryEntry der Value eine KarteiKarte ist und du diese Instanz in verschiedenen Listen/HashTables/Whatever hinzufügst, sind die Änderungen natürlich überall vorhanden.

Kurz: du hast nur eine Instanz einer KarteiKarte in verschiedenen Listen.

KarteiKarte myCard1 = new KarteiKarte();  
  
myCard1 = NewCards[schluessel] as KarteiKarte;  
  

Hier hat das new() übrigens nie einen Einfluss, da die neue Karte ja wieder überschrieben wird.

W
Wichter Themenstarter:in
6 Beiträge seit 2018
vor 6 Jahren

Vielen Dank für den Hinweis. Wie erstelle ich eine eigene Instanz einer kopierten Liste? Tut mir leid, wenn das eine dumme Frage ist, aber ich habe jetzt schon alles Mögliche ausprobiert, es bleibt stets dabei: Änderungen hier werden auch dort übernommen.

16.842 Beiträge seit 2008
vor 6 Jahren

new() erzeugt Instanzen; zB. eben new List();

Je nachdem von was wir alles sprechen erzeugen auch sogenannte Materialisierungen neue Instanzen, da im Hintergrund new() verwendet wird.

Bei Listen ist das zB.

var list2 = list2.ToList().

Die Liste2 ist nun eine neue Instanz.
Beide Listen haben aber die identische Instanzen der Elemente (sofern es sich um Referenztypen in der Liste handelt).
Die Elemente in der Liste werden also nicht neu erstellt.

W
Wichter Themenstarter:in
6 Beiträge seit 2018
vor 6 Jahren

Und wie schaffe ich es, dass neue Instanzen erzeugt werden, so dass ich die einen ändern kann ohne dass sich die anderen ändern?

W
Wichter Themenstarter:in
6 Beiträge seit 2018
vor 6 Jahren
NewCards = thisCards.Clone() as Hashtable;

... funktioniert auch nicht.

16.842 Beiträge seit 2008
vor 6 Jahren

Einfach rumprobieren oder rum stochern bringt selten zufriedenstellende Resultate. =)
Hast Du überhaupt mal geschaut, was Clone macht? Sicherlich nicht das, was Du an dieser Stelle willst.

Was Du viel eher willst ist sinngemäß entweder

var newList= new List<Card>(oldList);

oder

var newList = new List<Card>(oldList.Select(card => card.Clone()));

Aber mir nicht 100% ersichtlich, was Du willst.
Sieht mir ehrlich gesagt auch nicht danach aus, dass Du 100% das im Code abbildest, was Du willst 😉

Ich bin in 99% der Fällen der Meinung, dass Clone auch nicht logisch das ist, was man will.
Riecht für mich häufig nach Konzeptfehler. In Reviews seh ich oft, dass damit andere Design-Fehler versucht werden zu beheben.

Willst Du mal das Große Ganze beschreiben, warum Du das alles klonen willst?
Es gibt durchaus sinnvolle Szenarien; vielleicht ist das hier eines - oder auch nicht.

PS: [Artikel] C#: Richtlinien für die Namensvergabe
Das macht es leichter.

Und: mit "funktioniert nicht" kann niemand was anfangen. Wir sehen weder Deinen vollständigen Code noch Deinen Bildschirm - geschweige denn eine potentielle Fehlermeldung. 😉
[Hinweis] Wie poste ich richtig? Punkt 5

W
Wichter Themenstarter:in
6 Beiträge seit 2018
vor 6 Jahren

Hallo!

Mit "funktioniert nicht", meine ich, dass Änderungen an Elementen in der einen Hashtable - "NewCard" - sich auch auf die andere Liste - "BackUp" auswirken - und das will ich verhindern. Wenn ich ein Element in "NewCards" ändere, soll sich das nicht auf das entsprechende Element in "BackUp" auswirken. Edit: Beide Listen werden aus demselben Parameter ("thisCards") der aufrufenden Funktion gebildet.

Dein Codebeispiel Nummer Eins habe ich so übernommen:

Hashtable BackUp = new Hashtable(thisCards);
Hashtable NewCards = new Hashtable(thisCards);

...ohne Erfolg (d.h. Änderungen in "NewCards" ändern auch "BackUp")

Dein Codebeispiel Nummer 2 konnte ich nicht übernehmen, da die von mir verwendete Klasse "Hashtable" keine Methode "Select" hat.

Ich will deswegen das Original erhalten (also Änderungen in "BackUp" verhindern), damit der User die Möglichkeit hat, vorgenommene Änderungen zu verwerfen.

16.842 Beiträge seit 2008
vor 6 Jahren

Weil Du hier erneut nur die Liste duplizierst und nicht die Inhalte.
Wie gesagt; Du stocherst hier nur rum, kopierst einfach Code ohne ihn zu verstehen - so bringt das nichts.

Select ist eine Erweiterungsmethode; dazu wird das using von System.Linq benötigt.
Einfach mal in die Doku schauen - dafür ist sie da 👍

Undo löst man nicht unbedingt mit einem Clone.
Das nennt sich (Generic) Memento Pattern.

3.003 Beiträge seit 2006
vor 6 Jahren

Du hast Karl, Heinz und Peter.

Die schiebst du in die Klasse 6a.
Außerdem schiebst du die drei in die AG "Programmieren".

Jetzt schneidest du Karl aus der 6a die Haare und wunderst dich, dass auch Karl in der AG "Programmieren" kurze Haare hat.

Überraschung!


var backupList = originalList.Select(p => (MyType)p.Clone()).ToList();

Je nach MyType unbedingt prüfen, ob evtl eine deep copy nötig ist.

LaTino

EDIT: dann stehst du natürlich vor der Aufgabe, herauszufinden, ob ein Objekt überhaupt geändert wurde (ob man es Speichern muss, zB). Und weil du nicht der erste bist, der so etwas machen will, gibt es seit >20 Jahren das Command-Entwurfsmuster, das solche umständlichen Transaktionen wie das Kopieren von Objekten elegant umgeht.

"Furlow, is it always about money?"
"Is there anything else? I mean, how much sex can you have?"
"Don't know. I haven't maxed out yet."
(Furlow & Crichton, Farscape)

4.942 Beiträge seit 2008
vor 6 Jahren

Hallo zusammen,

hier geht es um den Unterschied zwischen Wert- und Verweistypen (bzw. Referenztypen).
Solange KarteiKarte eine Klasse (und keine Struktur) ist, werden in die Hashtable immer nur Referenzen eingetragen, d.h. Änderungen an den Eigenschaften eines Objekts wirken sich auf alle Referenzen aus.

PS: Hashtable ist veraltet, nutze besser die generische Klasse Dictionary<K, V>.

5.658 Beiträge seit 2006
vor 6 Jahren

Hi Wichter,

da es hier um grundlegene Funktionsweisen der Objektorientierten Programmierung geht, empfehle ich einen Blick in [FAQ] Wie finde ich den Einstieg in C#? sowie das OpenBook vom Rheinwerk-Verlag: 🛈

@Th69: Hashtables und Dictionarys sind zwei verschiedene Paar Schuhe für unterschiedliche Anwendungsfälle. Veraltet ist nur die untypisierte Variante.

Weeks of programming can save you hours of planning

4.942 Beiträge seit 2008
vor 6 Jahren

Hallo MrSparkle,

genau, er benutzt ja die untypisierte Hashtable...
Und der Ersatz ist eben (ab .NET 2.0!) das Dictionary.

5.658 Beiträge seit 2006
vor 6 Jahren

Hi Th69,

du hast natürlich Recht. HashTables hatte ich gar nicht mehr auf dem Schirm, und habe stattdessen HashSet gelesen. Sorry für das Mißverständnis 8)

Weeks of programming can save you hours of planning