Entwickler-Ecke
Basistechnologien - Eigenschaft einer Eigenschaft überwachen???
C# - Fr 10.10.14 08:36
Titel: Eigenschaft einer Eigenschaft überwachen???
Morgähn,
gibt es eine Möglichkeit, den setter einer Eigenschaft auszulösen, wenn eine Eigenschaft dieser Eigenschaft geändert wird? :gruebel:
Z.B. habe ich eine Klasse Rect. In dieser Klasse befindet sich ein Point Position. Kann ich in der Rect-Klasse prüfen ob die X oder Y Koordinate von Position geändert wurde?
freak4fun - Fr 10.10.14 09:21
Ja. Vor dem setzen den neuen mit dem alten wert vergleichen. :think:
C# - Fr 10.10.14 09:27
Ja clever :D
Der setter wird aber nicht aufgerufen! Genau das ist ja mein Problem. Meine Klasse bekommt nicht mit, wenn der Wert geändert wird.
Palladin007 - Fr 10.10.14 09:31
Setze doch einfach den gleichen Wert nochmal?
Dann wird der Setter aufgerufen, wobei das meiner Meinung nach aber irgendwie falsch klingt.
Warum genau soll der Setter denn aufgerufen werden?
Zeig mal ein Code-Beispiel, das verstehe ich glaube ich besser :D
freak4fun - Fr 10.10.14 09:31
C#-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19:
| public class MyClass { private Point _Position; public Point Position { set { if(this._Position.X != value.X) { MessageBox.Show("X hat sich verändert!"); } if(this._Position.Y != value.Y) { MessageBox.Show("Y hat sich verändert!"); } this._Position = value; } get { return this._Position; } } } |
C#-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22:
| public partial class Form1 : Form {
MyClass mc;
public Form1() { InitializeComponent(); }
private void button1_Click(object sender, EventArgs e) { Point p = new Point( int.Parse(textBox1.Text), int.Parse(textBox2.Text)); this.mc.Position = p; }
private void Form1_Load(object sender, EventArgs e) { mc = new MyClass(); } } |
Ralf Jansen - Fr 10.10.14 09:49
Das wird nicht funktionieren. Schon deshalb weil Point ein struct ist.
Der Zugriff auf den getter wird dir eine Kopie des Point liefern. Diese Kopie hat keinerlei Bezug zu ihrer Quelle.
Bei structs solltest du am besten immer davon ausgehen das die unveränderlich sind. Heißt wenn du die Position ändern willst ist es ein neuer Point struct.
Das Problem kannst du zum Beispiel ausprobieren wenn du versuchst in Winforms ein Control zu verschieben. Control.Location.X = neuer Wert wird zu keiner Veränderung führen da der Zugriff auf Control.Location dir bereits eine Kopie liefert. Und das ändern der Kopie natürlich keinerlei Auswirkung hat.
Dein Eigenschaften von Eigenschaften Überwachen Problem kannst du eigentlich nur mit einer eigene Klasse (kein struct) lösen das passende Events wirft die von der ~Owner~ Klasse gefangen werden und dann entsprechend ausgewertet wird.
C# - Fr 10.10.14 10:02
Das dachte ich mir schon... Aber mit events möchte ich nicht arbeiten. Dann mache ich aus meiner klasse eine Struktur das geht in Ordnung. Grund des ganzen ist, dass ich eine neue Klasse für rectangle erstelle. Die Positionen sind als Vektoren angegeben (auch eigene klasse).
Ralf Jansen - Fr 10.10.14 11:26
Zitat: |
Das dachte ich mir schon... Aber mit events möchte ich nicht arbeiten. |
Das ist vermutlich auch besser so ;)
Das verdrahten von Events bzw. das nicht benutzen von structs (aka keine Kopien) hätte dann andere Probleme wenn man das Objekt versuchst an anderer Stelle wiederzuverwenden und man eben keine Kopie hat.
C#-Quelltext
1: 2: 3: 4: 5: 6:
| var one = new MyClass(); one.Position = new Point(5,5); var two = new MyClass() two.Position = one.Position;
two.Position.X = 10; |
Je nachdem wie ~intelligent~ man das geregelt hat würde sich nun one and two verschieben oder nur one. Beides wäre offensichtlich falsch. Das Point Kopiersemantik benutzt ist gut so ;)
freak4fun - Fr 10.10.14 11:48
Ralf Jansen hat folgendes geschrieben : |
C#-Quelltext 1: 2: 3: 4: 5: 6:
| var one = new MyClass(); one.Position = new Point(5,5); var two = new MyClass() two.Position = one.Position;
two.Position.X = 10; |
Je nachdem wie ~intelligent~ man das geregelt hat würde sich nun one and two verschieben oder nur one. Beides wäre offensichtlich falsch. Das Point Kopiersemantik benutzt ist gut so ;) |
Wie kommst du darauf?
C#-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9:
| var one = new MyClass(); one.Position = new Point(5,5); var two = new MyClass(); two.Position = one.Position;
two.Position = new Point(10, 5);
MessageBox.Show("two.Position.X: " + two.Position.X.ToString()); MessageBox.Show("one.Position.X: " + one.Position.X.ToString()); |
Zitat: |
"two.Position.X: 10"
"one.Position.X: 5" |
Wo ist das Problem?
Ralf Jansen - Fr 10.10.14 12:02
Zitat: |
two.Position = one.Position;
two.Position = new Point(10, 5); |
a.) Da ist die erste Zuweisung natürlich unnötig.
und
b.) impliziert die Anforderung von C# das Point nicht kopiert wird bei Zuweisung was es im Moment tut da es ein struct ist. Wenn es nicht kopiert wird würde aber one und two auf die gleiche Point Instanz schauen. Sie hätten also gar keine unabhängige Position. Nachvollziehen kannst du das natürlich nicht mit der jetzigen Point struct sondern nur indem du Point nachschreibst als Klasse. Dann wäre C# Wunsch nähernungsweise erfüllbar. Es würden sich aber dafür andere Merkwürdigkeiten/Probleme einschleichen wie zum Beispiel die von mir gezeigte.
Palladin007 - Fr 10.10.14 13:25
Ralf, warum funktioniert das oben genannte Beispiel von C# nicht?
Natürlich wird im Speicher eine neue Point-"Instanz" erzeugt, wenn eine der Properties geändert wird, allerdings soll doch nur verglichen werden.
So wie ich das sehe, möchte er, dass eine Meldung ausgegeben wird, dass sich bei der Zuweisung des neuen Wertes tatsächlich etwas geändert hat.
Das kenne ich, wenn ich INotifyPropertyChanged implementiere. Ich prüfe erst, ob der ursprüngliche Wert ungleich dem neuen Wert ist. Wenn dem so ist, wird geändert und PropertyChanged geworfen, andernfalls nicht.
In diesem Fall soll das genauso ablaufen (nur mit einer MessageBox), allerdings soll nicht nur angezeigt werden, dass sich etwas ändert, sondern was im Inhalt sich konkret geändert hat.
Oder sehe ich da etwas falsch?
freak4fun - Fr 10.10.14 13:34
Ich hab es so verstanden, das er eine Variable vom Typ Point hat und nun einfach informiert werden will wenn sich daran was ändert. Der Rest ist Beiwerk. :roll:
Wenn er einen Point in seiner Klasse hat ist das immer nur eine Kopie des Original-Point. Da bekommt er auch keine Meldung wenn sich der Original-Point ändert.
Entweder er kapselt den Zugriff auf den Original-Point oder überdenkt sein Konzept.
Ralf Jansen - Fr 10.10.14 14:25
Zitat: |
Ralf, warum funktioniert das oben genannte Beispiel von C# nicht? |
Das Beispiel das er zeigt hat auch kein Problem. Es zeigt ja nicht was er mit Position machen will ;)
Ich kann nur zwischen den Zeilen lesen das er sowas wollte wie
MyClass.Position.X = 5; und dann im Position Setter irgendwie auf die Änderung von x zu reagieren.
Die Möglichkeit der Zuweisung eines neuen Points ist davon unberührt da kann man natürlich irgendwelche Test in den Setter einbauen ob sich der neue Point vom alten unterscheidet. Aber das war ja der Ausgangspunkt der Frage das sein Setter nicht aufgerufen wurde was nur Sinn macht wenn er auf erstgenannten aus war ohne Zuweisung eines neuen Point.
Palladin007 - Fr 10.10.14 15:07
Das würde nur via INotifyPropertyChanged gehen, man kann Interfaces doch auch in ein Struct implementieren?
Allerdings würde das dann eine neue Point-Struktur fordern.
Oder das ganze wird mit einer Klasse umgangen, die ICloneable implementiert und bei dem Setzen einer Instanz in eine Property wird nicht die tatsächliche Eigenschaft gesetzt, sondern eine Kopie davon.
Ralf Jansen - Fr 10.10.14 16:44
Klar wär das möglich aber du läufst dann eben in andere Probleme die ich mit dem Beispiel, 2 Klassen teilen sich plötzlich eine Position, zeigen wollte.
Du kannst dann die Klasse nicht einfach unbedacht woanders verwenden weil da eben noch eine Eventverdrahtung lebt und dann unerwartete Dinge passieren.
Das Problem kann diese potentielle neue Point Klasse auch nicht aus sich selber heraus lösen. Alle beteiligten Klassen müßten dann so clever sein beim zuweisen je nach Notwendigkeit mal doch eine Kopie anzufertigen oder Events zu verdrahten bzw. abzuhängen. Ist denke das ist zu fehleranfällig.
Die Ursünde war Point überhaupt Setter für X und Y zu geben. Ohne die wäre klar das ändern nur über den Konstruktor geht und auch nur das sinnvoll ist. Aber nachher ist man immer schlauer ;)
C# - Fr 10.10.14 17:09
Alsooooo um hier mal aufzuräumen:
Ich habe eine Klasse (mittlerweile ist es eine Struktur):
C#-Quelltext
1: 2: 3: 4: 5: 6: 7: 8:
| struct Vector2 { public float X {get; set;}
publif float Y {get; set;} ... } |
und
C#-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34:
| class Rect { public Vector2 Position { get { return position; } set { if (position == value) return;
position = value; center = value + Size / 2; topRight = new Vector2(value.X + Size.X, value.Y); bottomLeft = new Vector2(value.X, value.Y + Size.Y); bottomRight = value + Size; } }
public Vector2 Center { get { return center; } set { if (center == value) return;
center = value; position = value - Size / 2; topRight = new Vector2(value.X + Width / 2, value.Y - Height / 2); bottomLeft = new Vector2(value.X - Width / 2, value.Y + Height / 2); bottomRight = value + Size / 2; } } }
... |
Und was ich gerne gehabt hätte ist, dass wenn die Position geändert wird (via Position.X bzw. Position.Y) der Setter der Position-Eigenschaft aufgerufen wird, damit ich die anderen Variablen updaten kann.
Ralf Jansen - Fr 10.10.14 17:27
Pragmatische Lösung a.) Setter von X und Y in Vector2 (dann Vector 2 als struct) entfernen somit nur noch Ändern über Konstruktor möglich und damit implizit Rect.Position nur änderbar durch neu zuweisen.
Pragmatische Lösung b.) Vector2 wie du es zeigst (als Klasse) aber dann sollte Vector2 wie Paladin vorschlägt INotifyPropertyChanged implementieren und Rect entsprechend die Events fangen und darauf reagieren lassen. Dann darf Rect aber auf keinen Fall noch einen Setter für Position haben sondern Rect sollte nur eine intern selbst erzeugte Vector2 Instanz per Getter veröffentlichen(deine jetzigen Prüfungen im setter gehören dann in den PropertyChanged EventHandler) . Die Probleme die durch das Verschieben einer Vector2 Instanz zwischen rect Klassen enststehen würden sind kaum zu beherschen und solltest du so verhindern.
Palladin007 - Fr 10.10.14 18:55
Nutzt du das für eine Oberfläche?
Für DataBinding (was ja INotifyPropertyChanged nutzt), würde ich dann vermutlich eine Klasse schreiben, die im Grunde genau das tut, wie Point und gleichzeitig eine Konvertierung in den eigentlichen Point-Wert anbietet. Dafür können ja Casting-Operatoren definiert werden.
Dann kannst du die Klasse ganz normal nutzen, wie jede Andere auch.
Die eigentliche Programm-Logik arbeitet mit dem Point-Wert (struct in .NET), während die GUI die Point-Referenz (eigene KLasse) bekommt.
Ich glaube, besser geht es nicht.
C# - Fr 10.10.14 20:12
Ich bleibe jetzt bei struct. Ich möchte auf keinen Fall events hier einbauen, weil diese Typen Grundbausteine meiner Bibliothek sind. Das sollen ganz einfache Datentypen bleiben, die nur für Berechnungen sind. Wie halt auch Point und Rectangle. Die sollten auch etwas performant sein und nicht bei jedem Aufruf 100 verkettete Methoden kreuz und quer aufrufen.
Palladin007 - Sa 11.10.14 02:10
Muss es denn zwingend Point sein?
Erstelle doch sonst eine eigene Struktur, wo die Eigenschaften Readonly sind.
Dann gibt es dort nichts zu überwachen.
C# - Sa 11.10.14 11:11
Wie kommst du denn jetzt auf Point? Das war nur ein Beispiel. Ich habe doch einen Codeausschnitt hier reingestellt. Genau so verwende ich ihn auch, da ist nix mit Point. Vector2 und Rect sind die Typen die verwendet werden.
Palladin007 - Sa 11.10.14 11:15
Hab ich übersehen, tut mir Leid.
Aber auch hier gibt es Setter, brauchst du die denn unbedingt?
In deinem Code-Schnipsel rechnest du ja im Konstruktor und brauchst die Eigenschaft gar nicht direkt ändern.
Dann können die Setter auch weg und sie müssen nicht überwacht werden.
Das war ja im Prinzip Ralfs pragmatische Lösung a
Th69 - Sa 11.10.14 12:03
Hallo zusammen,
ja, ich sehe es auch so.
Einen immutablen Datentyp erzeugen:
C#-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12:
| struct Vector2 { public Vector2(float x, float y) { X = x; Y = y; }
public float X { get; private set; }
public float Y { get; private set; } } |
C# - Sa 11.10.14 13:00
Ja ist ein älteres Codeschnipsel. Beim Aktuellen habe ich die Setter privat gemacht.
Entwickler-Ecke.de based on phpBB
Copyright 2002 - 2011 by Tino Teuber, Copyright 2011 - 2025 by Christian Stelzmann Alle Rechte vorbehalten.
Alle Beiträge stammen von dritten Personen und dürfen geltendes Recht nicht verletzen.
Entwickler-Ecke und die zugehörigen Webseiten distanzieren sich ausdrücklich von Fremdinhalten jeglicher Art!