Autor |
Beitrag |
C#
Beiträge: 561
Erhaltene Danke: 65
Windows 10, Kubuntu, Android
Visual Studio 2017, C#, C++/CLI, C++/CX, C++, F#, R, Python
|
Verfasst: Fr 10.10.14 08:36
Morgähn,
gibt es eine Möglichkeit, den setter einer Eigenschaft auszulösen, wenn eine Eigenschaft dieser Eigenschaft geändert wird?
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?
_________________ Der längste Typ-Name im .NET-Framework ist: ListViewVirtualItemsSelectionRangeChangedEventHandler
|
|
freak4fun
Beiträge: 604
Erhaltene Danke: 4
Win 7 Pro
VS 2013 Express, Delphi, C#, PHP, Java
|
Verfasst: Fr 10.10.14 09:21
Ja. Vor dem setzen den neuen mit dem alten wert vergleichen.
_________________ "Ich werde auf GAR KEINEN Fall…!" - "Keks?" - "Okay, ich tu's."
i++; // zaehler i um 1 erhoehen
|
|
C#
Beiträge: 561
Erhaltene Danke: 65
Windows 10, Kubuntu, Android
Visual Studio 2017, C#, C++/CLI, C++/CX, C++, F#, R, Python
|
Verfasst: Fr 10.10.14 09:27
Ja clever
Der setter wird aber nicht aufgerufen! Genau das ist ja mein Problem. Meine Klasse bekommt nicht mit, wenn der Wert geändert wird.
_________________ Der längste Typ-Name im .NET-Framework ist: ListViewVirtualItemsSelectionRangeChangedEventHandler
|
|
Palladin007
Beiträge: 1282
Erhaltene Danke: 182
Windows 11 x64 Pro
C# (Visual Studio Preview)
|
Verfasst: 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
|
|
freak4fun
Beiträge: 604
Erhaltene Danke: 4
Win 7 Pro
VS 2013 Express, Delphi, C#, PHP, Java
|
Verfasst: 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(); } } |
_________________ "Ich werde auf GAR KEINEN Fall…!" - "Keks?" - "Okay, ich tu's."
i++; // zaehler i um 1 erhoehen
|
|
Ralf Jansen
Beiträge: 4701
Erhaltene Danke: 991
VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
|
Verfasst: 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.
Für diesen Beitrag haben gedankt: C#
|
|
C#
Beiträge: 561
Erhaltene Danke: 65
Windows 10, Kubuntu, Android
Visual Studio 2017, C#, C++/CLI, C++/CX, C++, F#, R, Python
|
Verfasst: 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).
_________________ Der längste Typ-Name im .NET-Framework ist: ListViewVirtualItemsSelectionRangeChangedEventHandler
|
|
Ralf Jansen
Beiträge: 4701
Erhaltene Danke: 991
VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
|
Verfasst: 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
Beiträge: 604
Erhaltene Danke: 4
Win 7 Pro
VS 2013 Express, Delphi, C#, PHP, Java
|
Verfasst: Fr 10.10.14 11:48
_________________ "Ich werde auf GAR KEINEN Fall…!" - "Keks?" - "Okay, ich tu's."
i++; // zaehler i um 1 erhoehen
|
|
Ralf Jansen
Beiträge: 4701
Erhaltene Danke: 991
VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
|
Verfasst: 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
Beiträge: 1282
Erhaltene Danke: 182
Windows 11 x64 Pro
C# (Visual Studio Preview)
|
Verfasst: 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
Beiträge: 604
Erhaltene Danke: 4
Win 7 Pro
VS 2013 Express, Delphi, C#, PHP, Java
|
Verfasst: 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.
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.
_________________ "Ich werde auf GAR KEINEN Fall…!" - "Keks?" - "Okay, ich tu's."
i++; // zaehler i um 1 erhoehen
|
|
Ralf Jansen
Beiträge: 4701
Erhaltene Danke: 991
VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
|
Verfasst: 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
Beiträge: 1282
Erhaltene Danke: 182
Windows 11 x64 Pro
C# (Visual Studio Preview)
|
Verfasst: 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
Beiträge: 4701
Erhaltene Danke: 991
VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
|
Verfasst: 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#
Beiträge: 561
Erhaltene Danke: 65
Windows 10, Kubuntu, Android
Visual Studio 2017, C#, C++/CLI, C++/CX, C++, F#, R, Python
|
Verfasst: 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
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.
_________________ Der längste Typ-Name im .NET-Framework ist: ListViewVirtualItemsSelectionRangeChangedEventHandler
|
|
Th69
Beiträge: 4764
Erhaltene Danke: 1052
Win10
C#, C++ (VS 2017/19/22)
|
Verfasst: Fr 10.10.14 17:20
Palladin007 hat folgendes geschrieben : | Das würde nur via INotifyPropertyChanged gehen, man kann Interfaces doch auch in ein Struct implementieren? |
Interfaces für Strukturen sind eine schlechte Idee, s. z.B. C#: structs and Interface
|
|
Ralf Jansen
Beiträge: 4701
Erhaltene Danke: 991
VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
|
Verfasst: 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
Beiträge: 1282
Erhaltene Danke: 182
Windows 11 x64 Pro
C# (Visual Studio Preview)
|
Verfasst: 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#
Beiträge: 561
Erhaltene Danke: 65
Windows 10, Kubuntu, Android
Visual Studio 2017, C#, C++/CLI, C++/CX, C++, F#, R, Python
|
Verfasst: 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.
_________________ Der längste Typ-Name im .NET-Framework ist: ListViewVirtualItemsSelectionRangeChangedEventHandler
|
|