Laden...

Undo auch für Basistypen implementieren [und immutable Objekte]

Erstellt von bigeddie vor 15 Jahren Letzter Beitrag vor 15 Jahren 2.299 Views
B
bigeddie Themenstarter:in
372 Beiträge seit 2007
vor 15 Jahren
Undo auch für Basistypen implementieren [und immutable Objekte]

Moin,

mal eine ganz bescheidene Frage:
Wie umgehe ich am geschicktesten das "ableiteverbot" bei Basistypen wie String, Int, ...

Will solche funktionen wie "undo"... implementieren

Viele Grüße

Ernst

Man muß nichts wissen,
man muß nur wissen wer es wissen könnte
oder wo es steht😉

5.742 Beiträge seit 2007
vor 15 Jahren

Hallo bigeddie,

Will solche funktionen wie "undo"... implementieren

Dann ist das Ableiten sicherlich ein falscher Weg.

Besser geeignet ist auf jeden Fall das Multilevel-Undo/-Redo mit dem Command-Muster

3.971 Beiträge seit 2006
vor 15 Jahren

Ansonsten, um eine Funktion nachträglich zu einer Klasse/Interface hinzuzufügen, kannst du auch extensions Methods verwenden.

Eine auch sehr gut geeignete Undo/Redo-Funktion ist unter Generic Memento Pattern for Undo-Redo in C# zu finden.

Es gibt 3 Arten von Menschen, die die bis 3 zählen können und die, die es nicht können...

B
bigeddie Themenstarter:in
372 Beiträge seit 2007
vor 15 Jahren

Nicht dass wir uns falsch verstehen,
Die Typen welche ich brauche, sollen z.B. auch Spaltennamen und Anzeigenamen enthalten um z.B. einen UPDATE-Befehl für SQL generieren zu können in dem ich neinfach nur das Objekt an einen generator übergebe...

Man muß nichts wissen,
man muß nur wissen wer es wissen könnte
oder wo es steht😉

5.742 Beiträge seit 2007
vor 15 Jahren

z.B. auch Spaltennamen und Anzeigenamen enthalten

Trotzdem ist Ableitung der falsche Weg.

Wie wäre es mit einer Komposition? Zum Beispiel:


public class Entry<T>
{
   public T Value { get; set; }
   public string ColumnName { get; set; }
   //...
} 

Allerdings bleibt die Frage, warum diese Informationen direkt in Verbindung mit dem Wert gespeichert werden müssen.

B
bigeddie Themenstarter:in
372 Beiträge seit 2007
vor 15 Jahren

Wenn ich eine Klasse habe, welche ihre Daten in eine Datenbank speichert und ich z.B. einen Updatestring(SQL) erstellen möchte ist es doch effizienter wenn ich nur die Daten updaten lasse, welche auch wirklich geändert wurden, oder?

Datatable arbeitet soweit ich verstanden habe nach einem ähnlichen Prinzip, sprich erstellt auch nur für die Daten die geändert wurden einen updatebefehl.

Man muß nichts wissen,
man muß nur wissen wer es wissen könnte
oder wo es steht😉

3.971 Beiträge seit 2006
vor 15 Jahren

Werttypen (auch Strings) im DOT NET können nicht feststellen, dass sie geändert wurden, da sie als Immutable implementiert worden sind (was an sich auch richtig ist).

Deshalb brauchst du eine Klasse, die Änderungen erkennt und diese auch festhält sowie den aktuellen Zustand an dein DataSet oder sonst wo ausliefert.

Hast du dir die geposteten Links mal angesehen? Alle beide enthalten entsprechende (unterschiedliche) Lösungen für dein Problem.

Es gibt 3 Arten von Menschen, die die bis 3 zählen können und die, die es nicht können...

4.207 Beiträge seit 2003
vor 15 Jahren

Werttypen (auch Strings) im DOT NET können nicht feststellen, dass sie geändert wurden, da sie als Immutable implementiert worden sind (was an sich auch richtig ist).

Das ist meines Erachtens so pauschal nicht korrekt.

string ist immutable, und Delegates zB auch. Aber ein int oder ein float ist doch nicht immutable?

Wissensvermittler und Technologieberater
für .NET, Codequalität und agile Methoden

www.goloroden.de
www.des-eisbaeren-blog.de

F
10.010 Beiträge seit 2004
vor 15 Jahren

@bigeddie:
Du willstr also den 187. ORMapper selber schreiben?

B
bigeddie Themenstarter:in
372 Beiträge seit 2007
vor 15 Jahren

Der Ansatz von winSharp93 scheint mir für die Aufgabe eigentlich am plausiebelsten.

Vielleicht erfinde ich ja auch das Rad nochmal neu, aber mir geht es darum zu sehen ob es funktionieren kann (nennt man glaube ich Grundlagenforschung auf unterstem Level).

Meine Intension ist es z.B. auch für Abfragen mit mehreren JOINS die Daten ändern zu können (z.B. beim Update die Änderungen wieder in die entsprechende Tabelle zurückschreiben zu können) ohne mir selbst ein Loch ins Knie zu schießen.
(Klar kann ich nur wenn die Namen der Spalten eindeutug sind....)

Man muß nichts wissen,
man muß nur wissen wer es wissen könnte
oder wo es steht😉

F
10.010 Beiträge seit 2004
vor 15 Jahren

Du kannst das höchsten über Proxies machen, wie dem CastelProxy oder dem Proxy von LinFu.

Aber das ist eher das Pferd von der "falschen" Seite aufzäumen.

Jojns werden auch in ORMappern meist mit verarbeitet, nur das sie dort
dann meist als eigenständige Objekte in dem DTO/BO auftauchen.
Deshalb bieten die meisten ORMapper ein LazyLoading ( lädt die Unterobjekte nach wenn sie benötigt werden )
oder ein EagerLoading ( alles wird gleich zusammen gelesen ) an.

Dadurch ist das schreiben wiederum einfach.

"Selbst" das EntityFramework bietet soetwas an.

Aber wenn Du es nur zu forschungszwecken machen willst, versuch mal die sache mit dem Proxy.
Bei Codeproject gibt es zu LinFu etwas dazu.

3.971 Beiträge seit 2006
vor 15 Jahren

string ist immutable, und Delegates zB auch. Aber ein int oder ein float ist doch nicht immutable?

Doch ich denke schon das Int, Float usw. immutable ist. Immutable bedeutet ja, das das Objekt (egal ob Klasse oder Struktur) keine Funktionen hat, die einen Wert von sich selbst ändern, sondern ein neues Objekt zurückgeben, mit neuem Wert.

Es gibt 3 Arten von Menschen, die die bis 3 zählen können und die, die es nicht können...

49.485 Beiträge seit 2005
vor 15 Jahren

Hallo Golo Roden,

Objekte macht man immutable, damit man sie in ein anderes Objekt übernehmen kann (Setter) bzw. sie aus einem anderen Objekt herausgeben kann (Getter), ohne es kopieren zu müssen und trotzdem die Kapselung des anderen Objekts nicht verletzt.

Wäre String nicht immutable, würde der folgende Code unter Verletzung der Kapselung den Zustand des Personen-Objektes ändern.

Person p = new Person ("Peter, Pan");
String name = p.Name;
name.Substring (0, 5); // Angenommen, String wäre nicht immutable
Console.WriteLine (p.Name); // Gäbe "Peter" aus

Nun ist diese Problematik bei Werttypen sowieso nicht in dem Maße gegeben, weil bei Werttypen durch eine Zuweisung immer der Inhalt kopiert wird.

Person p = new Person (new DateTime (1892, 5, 1));
DateTime dateOfBirth = p.DateOfBirth;
dateOfBirth.Year = 2000; // Angenommen, DateTime wäre nicht immutable
Console.WriteLine (p.DateOfBirth); // Gäbe trotzdem immer noch 01.05.1892 aus

Trotzdem würde ich es so sehen, wie kleines_eichhoernchen. Immutable ist ein Objekt dann, wenn es keine Properties und Methoden anbietet, die seinen Zustand ändern.

herbivore

4.207 Beiträge seit 2003
vor 15 Jahren

Okay, auch die MSDN sagt zu Int32

"Members that appear to modify instance state actually return a new instance initialized with the new value."

Das heißt für mich dann quasi "immutable".

Wissensvermittler und Technologieberater
für .NET, Codequalität und agile Methoden

www.goloroden.de
www.des-eisbaeren-blog.de

143 Beiträge seit 2008
vor 15 Jahren

In object-oriented and functional programming, an immutable object is an object whose state cannot be modified after it is created

Int32 (Framework Class Library)
MSDN: Int32 Structure

Thread Safety
All members of this type are thread safe. Members that appear to modify instance state actually return a new instance initialized with the new value. As with any other type, reading and writing to a shared variable that contains an instance of this type must be protected by a lock to guarantee thread safety.

Ist also immutable!


            int i = 4;
            int a = i;
            i = 6;
            Console.WriteLine(a); // >> 4
            Console.ReadLine();

Gruß Timo

49.485 Beiträge seit 2005
vor 15 Jahren

Hallo zusammen,

Members that appear to modify instance state actually return a new instance initialized with the new value.

hm, ich will mir ja nicht selbst widersprechen und tue das auch nicht, denn ich hatte ja immutable etwas weniger strikt definiert. Trotzdem halte ich diese Aussage aus der MSDN für falsch. Immerhin gibt es Operatoren wie ++, += u.ä. Und die ändern bei int & Co doch den Wert. Aber in Bezug auf Enthaltensein und Properties, wie ich das oben beschrieben habe, ändert das nichts an dem von mir Gesagten.

herbivore

143 Beiträge seit 2008
vor 15 Jahren

Wäre nach deiner Aussage nicht auch String mutable?


            String v = "asd";
            v += "a";
            Console.WriteLine(v); // >> asda

Gruß Timo

143 Beiträge seit 2008
vor 15 Jahren

Werttypen:
MSDN: Werttypen (C#-Referenz)

Unterteilen sich wie folgt:1.Strukturen
a Numerische Typen
b Ganzzahlige Typen
c Gleitkommatypen
d decimal
e bool
f Benutzerdefinierte Strukturen

1.Enumerationen

Alle Werttypen werden implizit von System.ValueType abgeleitet.

MSDN: ValueType-Klasse

  
Vererbungshierarchie   
  System.Object  
    -System.ValueType  

Na komisch ich dachte immer Strukturen wären keine Objekte! *g ValueType überschreibt nur viel von Object. Keine Ahnung was das jetzt genau bedeutet bin verwirrt! *g

Gruß Timo

49.485 Beiträge seit 2005
vor 15 Jahren

Hallo Omit,

Wäre nach deiner Aussage nicht auch String mutable?

nein, durch += wird das Originale-String-Objekt nicht geändert, sondern es wird ein neues Objekt erzeugt. Die Referenz auf das neue Objekt landet in der Variablen v.

Na komisch ich dachte immer Strukturen wären keine Objekte!

Das kommt darauf an, was genau man unter dem Begriff Objekt versteht. Der wichtige Unterschied ist, dass bei einem Werttyp in einer Variablen der Wert/das Objekt und bei einem Referenztyp in einer Variablen eine Referenz auf den Wert/das Objekt gespeichert wird.

herbivore

143 Beiträge seit 2008
vor 15 Jahren

Ich kenne schon den Unterschied von Referenz-Typen und Wertetypen. Doch hat mich das es etwas irritiert, dass auch ValueTypes von Object erbt, da ich das so nicht erwartet hätte. Folgende Erklärung ergibt in dem Hinblick nur noch bedingt Sinn.

Value Types:

In C#, all the "things" declared with the following list of type declarations are Value types (because they are from System.ValueType):

bool
...
ushort

Reference Types:

All the "things" declared with the types in this list are :::

class
interface
delegate
object
string

Ach jetzt verstehe ich, was du meinst.(Glaube ich zumindest 😉)

Da Int32 direkt auf den Heap verweist und nicht wie String erst auf den Stack.
Es wird mit += oder so, kein neuer Bereich auf dem Heap allokiert, sondern der bestehende Eintrag auf dem Heap geändert.
Wo hingegen bei String eine neue String-Referenz im Stack angelegt wird, die auf einen neu allokierten Bereich im Heap verweist.

So langsam frag ich mich ob es überhaupt sinnvoll ist sich darüber zu Unterhalten ob ein Valuetype immutable ist.
Aber im Prinzip verhält sich Int32 wie ein Immutable Object.
Das ganze wird jetzt etwas philosophisch und hängt "nur"(*g) von der Art ab wie man immutable defeniert.

Aber wie sieht es mit boxed Int aus:
int? a = 0;
Sieht für mich auch immutable aus.

Gott so Details können ganz schon verwirrend sein, geht das nur mir so?

Gruß Timo

49.485 Beiträge seit 2005
vor 15 Jahren

Hallo Omit,

Es wird mit += oder so, kein neuer Bereich auf dem Heap allokiert, sondern der bestehende Eintrag auf dem Heap geändert.

im Prinzip ja, auch wenn du in deinem Beitrag überall Heap und Stack vertauschen müsstest, damit es halbwegs hinhaut. 🙂

So langsam frag ich mich ob es überhaupt sinnvoll ist sich darüber zu Unterhalten ob ein Valuetype immutable ist.

Das hängt eben von der Definition ab. Natürlich kann man eine Werttyp-Variable durch Zuweisung ändern. Aber ich hatte immutable ja extra so definiert, dass es keine Properties und Methoden gibt, die das Objekt ändern. Ich habe jetzt mal genauer geguckt. Dass a += b in a = a + b umgesetzt wird, hatte ich sowieso vermutet. += ist somit eigentlich kein Sonderfall, sondern letztendlich nur eine Änderung durch Zuweisung. Bei ++i hatte ich allerdings nicht vermutet, dass das in i = i + 1 umgesetzt wird, sondern hatte eher eine extra Increment-Operation erwartet. Allerding erzeugt ++i tatsächlich genau den gleichen IL-Code wie i = i + 1. Somit auch hier kein Sonderfall, sondern wieder nur eine Änderung durch Zuweisung. Insofern ziehe ich

Trotzdem halte ich diese Aussage aus der MSDN für falsch. Immerhin gibt es Operatoren wie ++, += u.ä. Und die ändern bei int & Co doch den Wert.

zurück und bleibt es also doch bei

Members that appear to modify instance state actually return a new instance initialized with the new value.

Abgesehen natürlich von der Zuweisung, aber das ist ja sowieso klar und für alle Werttypen gleich.

Aber wie sieht es mit boxed Int aus:
int? a = 0;
Sieht für mich auch immutable aus.

Jipp!

herbivore

O
778 Beiträge seit 2007
vor 15 Jahren

Aber wie sieht es mit boxed Int aus:
int? a = 0;
Sieht für mich auch immutable aus.

Hm, also meiner Auffassung nach ist boxed int schon mal was ganz anderes als int?. Boxed int wäre eher sowas:

Object a = 0;

also die Zuweisung einer ValueType-Konstante (0) zu einer Variable eines Typs, der nicht von ValueType erbt, wodurch für diese Konstante Speicher auf dem Heap reserviert werden muss, während

int? a = 0;

eine Zuweisung der 0 zu einer Nullable<int> Struktur ist, die ganz ähnlich zu int von ValueType abgeleitet ist und der Begriff immutable damit Definitionssache ist. Nach untenstehender Definition wäre int? immutable, ja.

Aber erstmal zu mutable und immutable: Man kann immutable ja auch definieren als alles, was mit dem hübschen Attribut ImmutableObjectAttribute markiert ist - mit der Folge, dass man alles, was in der mscorlib steht schon mal vorweg ausklammert.

Meine Definition für mutable wäre die folgende: Eine Klasse heißt mutable, wenn sie als nicht vererbbar gekennzeichnet ist, nicht abstrakt ist und mindestens eine nicht statische, öffentliche Methode existiert, für die Parameter existieren, sodass diese Methode mindestens ein Feld (und damit den inneren Zustand des Objekts) derselben Instanz verändert. Nach IL-Sinn sind allerdings auch Property-Setter als Methoden zulässig.

Leider kann man die Implementierung von String im Reflector nicht sehen, aber anhand der Tatsache, dass keine öffentliche Methode von String dieselbe String-Instanz verändert sondern wenn schon denn schon eine neue String-Instanz zurückgibt, kann man schon sagen, dass String immutable ist. Bei Int16, Int32, Int64 und wie sie alle heißen sieht die Sache nicht anders aus, auch die sind immutable.

Interessanterweise sind aber nicht alle von ValueType erbenden Objekte immutable. Alle Enums sind immutable und alle in der mscorlib definierten Zahlendatentypen auch - aber System.Drawing.Size aus der System.Drawing ist beispielsweise mutable, allerdings sehe ich nicht wirklich einen Grund dafür, aber auch keinen dagegen (außer vielleicht, dass es erstmal verwirend ist).

Ach ja und

Es wird mit += oder so, kein neuer Bereich auf dem Heap allokiert

Doch, mit += wird neuer Speicher allokiert, da wie herbivore in seinem Beitrag auch geschrieben hat a += b zu a = a + b umgesetzt wird.

143 Beiträge seit 2008
vor 15 Jahren

Hi herbivore,

im Prinzip ja, auch wenn du in deinem Beitrag überall Heap und Stack vertauschen müsstest, damit es halbwegs hinhaut. 🙂

ich war mir schon unsicher, hätte es dann doch eher nochmal nachschauen sollen! 😉

Gruß Timo

3.971 Beiträge seit 2006
vor 15 Jahren

System.Drawing.Size aus der System.Drawing ist beispielsweise mutable, allerdings sehe ich nicht wirklich einen Grund dafür

Wenn du eine immutable-Size Struktur haben möchtest, musst du entweder sie als quasi readonly implementieren und mit Operatoren überladen oder aber du musst eine readonly property haben und eine extra Funktion (kein Set-Property) um den neuen Wert zu setzen. Beispiel:


struct Size {
  private int x;
  public int X { get { return x; } }
  public Size SetX(int value) { return new Size(value, y); }

  private int y;
  public int Y { get { return y; } }
  public Size SetY(int value) { return new Size(x, value); }

  public Size(int x, int y) { this.x = x; this.y = y; }
}

Es gibt 3 Arten von Menschen, die die bis 3 zählen können und die, die es nicht können...

O
778 Beiträge seit 2007
vor 15 Jahren

Angenommen, ich will einem Objekt, was eine Size-Property bereitstellt eine neue Höhe verpassen, dann ist ja klar, dass sowas

obj.Size.Height = newHeight;

nicht zum Ziel führt (teilweise merkt der Compiler das sogar und meckert rum). Also muss man die Struktur erstmal in einer Variablen zwischenspeichern:

Size s = obj.Size;
s.Height = newHeight;
obj.Size = s;

Das setzt aber voraus, dass Size mutable ist. Aber es geht ja bei einer so simplen Struktur auch deutlich einfacher, ohne zusätzliche Variable und ohne unnötige Voraussetzungen, ob Size jetzt mutable oder nicht ist:

obj.Size = new Size(obj.Size.Width, newHeight);

Ich frage mich lediglich, warum man dann nicht gleich alle Strukturen, die nur zwei Eigenschaften haben, als immutable implementiert hat. Weil das eigentliche Problem ist, dass man, wenn die Struktur mutable ist, auch über die Variante ganz oben nachdenken könnte, und die folgt zwangsläufig zu einem Bug.

//edit: schließenden CSHARP-Tag vergessen

B
bigeddie Themenstarter:in
372 Beiträge seit 2007
vor 15 Jahren

Hi it´s me again😉

Also um nochmal auf das eigentliche Thema zurückzukommen...

Ich müsste also quasi für jeden Basistyp einen eigenen Type oder nach dem Code den winSharp93 gepostet hat, erstellen in welchem ich dann die Anforderungen, welche ich an den Type habe (z.B. ein Pattern welches mir die Validität des im entsprechenden String-Typs z.B.CSString abzulegenden Wertes z.B. in einer Seter-Methode prüft und mir ggf. eine Exception generiert wenn der übergebene String nicht valide ist?

Undo wäre dadurch auch problemlos möglich....

Viele Grüße

Bigeddie

Man muß nichts wissen,
man muß nur wissen wer es wissen könnte
oder wo es steht😉

3.971 Beiträge seit 2006
vor 15 Jahren

Hallo bigeddie,
an sich wurden schon (mind.) 2 Links hier im Thread gepostet, die dein Problem lösen und auch klären, warum und wieso. Hast du dir die Links schonmal angeschaut?

Es gibt 3 Arten von Menschen, die die bis 3 zählen können und die, die es nicht können...