Microsoft hätte etwas Bissiges implementieren sollen INotifyPropertyChanged
, wie in den automatischen Eigenschaften. Geben {get; set; notify;}
Sie einfach an, dass es meiner Meinung nach sehr sinnvoll ist, dies zu tun. Oder gibt es irgendwelche Komplikationen, um es zu tun?
Können wir selbst so etwas wie "Benachrichtigen" in unseren Eigenschaften implementieren? Gibt es eine anmutige Lösung für die Implementierung INotifyPropertyChanged
in Ihrer Klasse oder die einzige Möglichkeit, dies zu tun, besteht darin, das PropertyChanged
Ereignis in jeder Eigenschaft auszulösen.
Wenn nicht, können wir etwas schreiben, um den Code automatisch zu generieren, um das PropertyChanged
Ereignis auszulösen ?
Antworten:
Ohne etwas wie Postsharp zu verwenden, verwendet die von mir verwendete Minimalversion Folgendes:
Jede Eigenschaft ist dann nur so etwas wie:
das ist nicht riesig; Wenn Sie möchten, kann es auch als Basisklasse verwendet werden. Die
bool
Rückgabe vonSetField
gibt an, ob es sich um ein No-Op handelt, falls Sie eine andere Logik anwenden möchten.oder noch einfacher mit C # 5:
was so genannt werden kann:
mit dem der Compiler das
"Name"
automatisch hinzufügt .C # 6.0 erleichtert die Implementierung:
... und jetzt mit C # 7:
quelle
[CallerMemberName]
Ab .Net 4.5 gibt es endlich eine einfache Möglichkeit, dies zu tun.
.Net 4.5 führt neue Anruferinformationsattribute ein.
Es ist wahrscheinlich eine gute Idee, der Funktion auch einen Vergleicher hinzuzufügen.
Weitere Beispiele hier und hier
Siehe auch Anruferinformationen (C # und Visual Basic).
quelle
Ich mag Marc's Lösung wirklich, aber ich denke, sie kann leicht verbessert werden, um die Verwendung einer "magischen Zeichenfolge" zu vermeiden (die kein Refactoring unterstützt). Anstatt den Eigenschaftsnamen als Zeichenfolge zu verwenden, können Sie ihn einfach zu einem Lambda-Ausdruck machen:
Fügen Sie einfach die folgenden Methoden zu Marc's Code hinzu, es wird den Trick machen:
Übrigens wurde dies von
dieseraktualisierten URL desBlogpostsinspiriertquelle
Es gibt auch Fody mit einem PropertyChanged- Add-In, mit dem Sie Folgendes schreiben können:
... und beim Kompilieren werden Benachrichtigungen über geänderte Eigenschaften eingefügt.
quelle
"Fody/.*?:",LogCustom2,True
, wird sie in der Farbe "Custom 2" hervorgehoben. Ich habe es hellrosa gemacht, damit es leicht zu finden ist. Fody einfach alles, es ist die sauberste Art, etwas zu tun, das viele sich wiederholende Eingaben enthält.Ich denke, die Leute sollten der Leistung etwas mehr Aufmerksamkeit schenken. Dies wirkt sich tatsächlich auf die Benutzeroberfläche aus, wenn viele Objekte gebunden werden müssen (denken Sie an ein Raster mit mehr als 10.000 Zeilen) oder wenn sich der Wert des Objekts häufig ändert (Echtzeitüberwachungs-App).
Ich nahm verschiedene Implementierungen, die hier und anderswo gefunden wurden, und führte einen Vergleich durch. Testen Sie den Leistungsvergleich von INotifyPropertyChanged-Implementierungen .
Hier ist ein Blick auf das Ergebnis
quelle
Ich stelle eine verbindliche Klasse in meinem Blog unter http://timoch.com/blog/2013/08/annoyed-with-inotifypropertychange/ vor. Bindable verwendet ein Wörterbuch als Eigenschaftstasche. Es ist einfach genug, die erforderlichen Überladungen für eine Unterklasse hinzuzufügen, um ihr eigenes Sicherungsfeld mithilfe von ref-Parametern zu verwalten.
Der Code:
Es kann folgendermaßen verwendet werden:
quelle
protected T Get<T>(T defaultValue, [CallerMemberName] string name = null)
und auch einzucheckenif (_properties.ContainsKey(name) && Equals(value, Get<T>(default(T), name)))
(um zu erhöhen und zu speichern, wenn es zum ersten Mal auf den Standardwert gesetzt wird)Ich hatte noch keine Gelegenheit, dies selbst zu versuchen, aber wenn ich das nächste Mal ein Projekt mit einer großen Anforderung für INotifyPropertyChanged einrichte, beabsichtige ich, ein Postsharp- Attribut zu schreiben, das den Code beim Kompilieren einfügt . Etwas wie:
Wird werden:
Ich bin mir nicht sicher, ob dies in der Praxis funktionieren wird und ich muss mich hinsetzen und es ausprobieren, aber ich verstehe nicht, warum nicht. Möglicherweise muss es einige Parameter für Situationen akzeptieren, in denen mehr als ein OnPropertyChanged ausgelöst werden muss (wenn ich beispielsweise eine FullName-Eigenschaft in der obigen Klasse hatte).
Momentan verwende ich eine benutzerdefinierte Vorlage in Resharper, aber trotzdem habe ich es satt, dass alle meine Eigenschaften so lang sind.
Ah, eine schnelle Google-Suche (die ich hätte machen sollen, bevor ich das geschrieben habe) zeigt, dass mindestens eine Person hier schon einmal so etwas gemacht hat . Nicht genau das, was ich mir vorgestellt hatte, aber nah genug, um zu zeigen, dass die Theorie gut ist.
quelle
Ja, es gibt sicherlich einen besseren Weg. Hier ist es:
Schritt für Schritt Tutorial von mir geschrumpft, basierend auf diesem nützlichen Artikel .
NotifierInterceptor
ProxyCreator
-
Binden Sie Bindungen in xaml:
Fügen Sie die Codezeile wie folgt in die CodeBehind-Datei MainWindow.xaml.cs ein:
DataContext = ProxyCreator.MakeINotifyPropertyChanged<MainViewModel>();
Beachtung!!! Alle begrenzten Eigenschaften sollten mit dem virtuellen Schlüsselwort dekoriert werden, da sie vom Castle-Proxy zum Überschreiben verwendet werden.
quelle
type
,interfaces to apply
,interceptors
.CreateClassProxy<T>
Methode verwendet. Ganz anders ... hmmm, ich frage mich, warum die generische Methode so begrenzt ist. :(Ein sehr AOP-ähnlicher Ansatz besteht darin, das INotifyPropertyChanged-Material im laufenden Betrieb auf ein bereits instanziiertes Objekt zu injizieren. Sie können dies mit etwas wie Castle DynamicProxy tun. Hier ist ein Artikel, der die Technik erklärt:
Hinzufügen von INotifyPropertyChanged zu einem vorhandenen Objekt
quelle
Schauen Sie hier: http://dotnet-forum.de/blogs/thearchitect/archive/2012/11/01/die-optimale-implementierung-des-inotifypropertychanged-interfaces.aspx
Es ist in deutscher Sprache geschrieben, aber Sie können die ViewModelBase.cs herunterladen. Alle Kommentare in der cs-Datei sind in englischer Sprache verfasst.
Mit dieser ViewModelBase-Klasse können bindbare Eigenschaften implementiert werden, die den bekannten Abhängigkeitseigenschaften ähneln:
quelle
Basierend auf der Antwort von Thomas, die aus einer Antwort von Marc übernommen wurde, habe ich den Code der reflektierten Eigenschaft in eine Basisklasse umgewandelt:
Die Verwendung entspricht der Antwort von Thomas, außer dass Sie zusätzliche Eigenschaften übergeben können, für die Sie benachrichtigen möchten. Dies war erforderlich, um berechnete Spalten zu verarbeiten, die in einem Raster aktualisiert werden müssen.
Ich habe dies eine Sammlung von Elementen steuern, die in einer BindingList gespeichert sind, die über eine DataGridView verfügbar gemacht wird. Ich muss keine manuellen Refresh () -Aufrufe mehr an das Grid senden.
quelle
Lassen Sie mich meinen eigenen Ansatz namens Yappi vorstellen . Es gehört zu den vom Runtime-Proxy abgeleiteten Klassengeneratoren und fügt einem vorhandenen Objekt oder Typ wie dem dynamischen Proxy von Caste Project neue Funktionen hinzu.
Es ermöglicht die einmalige Implementierung von INotifyPropertyChanged in der Basisklasse und die Deklaration abgeleiteter Klassen im folgenden Stil, wobei INotifyPropertyChanged weiterhin für neue Eigenschaften unterstützt wird:
Die Komplexität der abgeleiteten Klassen- oder Proxy-Konstruktion kann hinter der folgenden Zeile verborgen werden:
Alle Implementierungsarbeiten von INotifyPropertyChanged können folgendermaßen ausgeführt werden:
Es ist völlig sicher für das Refactoring, verwendet keine Reflexion nach der Typkonstruktion und ist schnell genug.
quelle
TDeclaration
TypparameterPropertyImplementation
? Sicherlich können Sie einen geeigneten Typ finden, um den Getter / Setter nur mit aufzurufen (nicht callvirt)TImplementation
?Alle diese Antworten sind sehr nett.
Meine Lösung besteht darin, die Codefragmente zu verwenden, um die Arbeit zu erledigen.
Dies verwendet den einfachsten Aufruf des PropertyChanged-Ereignisses.
Speichern Sie dieses Snippet und verwenden Sie es, während Sie das 'fullprop'-Snippet verwenden.
Sie können den Anruf nach Belieben ändern (um die oben genannten Lösungen zu verwenden).
quelle
Wenn Sie in .NET 4.5 Dynamik verwenden, müssen Sie sich keine Sorgen machen
INotifyPropertyChanged
.Wenn Name an ein Steuerelement gebunden ist, funktioniert es einwandfrei.
quelle
Eine andere kombinierte Lösung verwendet StackFrame:
Verwendungszweck:
quelle
get_Foo
Methode im Freigabemodus möglicherweise ausgeblendet wird .Ich habe in meiner Basisbibliothek eine Erweiterungsmethode zur Wiederverwendung erstellt:
Dies funktioniert mit .Net 4.5 aufgrund von CallerMemberNameAttribute . Wenn Sie es mit einer früheren .NET-Version verwenden möchten, müssen Sie die Methodendeklaration von:
...,[CallerMemberName] string propertyName = "", ...
in ändern...,string propertyName, ...
Verwendungszweck:
quelle
Ich habe auf diese Weise gelöst (es ist ein bisschen mühsam, aber es ist sicherlich das schnellere in der Laufzeit).
In VB (sorry, aber ich denke, es ist nicht schwer, es in C # zu übersetzen) mache ich diese Ersetzung durch RE:
mit:
Diese Transofrm alle Code wie folgt:
Im
Und wenn ich einen besser lesbaren Code haben möchte, kann ich das Gegenteil sein, indem ich nur die folgende Ersetzung vornehme:
Mit
Ich werfe, um den IL-Code der set-Methode zu ersetzen, aber ich kann nicht viel kompilierten Code in IL schreiben ... Wenn ich ihn eines Tages schreibe, sage ich es Ihnen!
quelle
Ich behalte das als Ausschnitt. C # 6 fügt eine nette Syntax zum Aufrufen des Handlers hinzu.
quelle
Hier ist eine Unity3D- oder Nicht-CallerMemberName-Version von NotifyPropertyChanged
Mit diesem Code können Sie Eigenschaftsunterstützungsfelder wie folgt schreiben:
Wenn Sie in Resharper ein Muster- / Such-Snippet erstellen, können Sie Ihren Workflow auch automatisieren, indem Sie einfache Requisitenfelder in den obigen Hintergrund konvertieren.
Suchmuster:
Muster ersetzen:
quelle
Ich habe einen Artikel geschrieben, der dabei hilft ( https://msdn.microsoft.com/magazine/mt736453) ). Sie können das SolSoft.DataBinding NuGet-Paket verwenden. Dann können Sie Code wie folgt schreiben:
Leistungen:
quelle
Obwohl es offensichtlich viele Möglichkeiten gibt, dies zu tun, mit Ausnahme der magischen AOP-Antworten, scheint keine der Antworten das Festlegen der Eigenschaft eines Modells direkt aus dem Ansichtsmodell zu betrachten, ohne dass ein lokales Feld zum Verweisen vorhanden ist.
Das Problem ist, dass Sie nicht auf eine Eigenschaft verweisen können. Sie können diese Eigenschaft jedoch mit einer Aktion festlegen.
Dies kann wie der folgende Code-Extrakt verwendet werden.
In diesem BitBucket-Repo finden Sie eine vollständige Implementierung der Methode und einige verschiedene Möglichkeiten, um dasselbe Ergebnis zu erzielen, einschließlich einer Methode, die LINQ verwendet, und einer Methode, die Reflektion verwendet. Beachten Sie, dass diese Methoden in Bezug auf die Leistung langsamer sind.
quelle
Andere Dinge, die Sie bei der Implementierung dieser Art von Eigenschaften berücksichtigen sollten, sind die Tatsache, dass INotifyPropertyChang * ed * beide Ereignisargumentklassen verwenden.
Wenn Sie eine große Anzahl von Eigenschaften haben, die festgelegt werden, kann die Anzahl der Instanzen von Ereignisargumentklassen sehr groß sein. Sie sollten in Betracht ziehen, sie zwischenzuspeichern, da sie einer der Bereiche sind, in denen eine Zeichenfolgenexplosion auftreten kann.
Schauen Sie sich diese Implementierung an und erklären Sie, warum sie konzipiert wurde.
Josh Smiths Blog
quelle
Ich habe gerade ActiveSharp - Automatic INotifyPropertyChanged gefunden . Ich habe es noch nicht verwendet, aber es sieht gut aus.
Um von seiner Website zu zitieren ...
Schreiben Sie stattdessen folgende Eigenschaften:
Beachten Sie, dass der Name der Eigenschaft nicht als Zeichenfolge angegeben werden muss. ActiveSharp findet das zuverlässig und korrekt heraus. Es funktioniert basierend auf der Tatsache, dass Ihre Eigenschaftsimplementierung das Hintergrundfeld (_foo) durch ref übergibt. (ActiveSharp verwendet diesen Aufruf "by ref", um zu identifizieren, welches Sicherungsfeld übergeben wurde, und anhand des Felds die Eigenschaft zu identifizieren.)
quelle
Eine Idee mit Reflexion:
quelle
Mir ist klar, dass diese Frage bereits unzählige Antworten hat, aber keine davon fühlte sich für mich richtig an. Mein Problem ist, dass ich keine Performance-Hits will und bereit bin, allein aus diesem Grund ein wenig Ausführlichkeit zu ertragen. Ich interessiere mich auch nicht allzu sehr für Auto-Eigenschaften, was mich zu der folgenden Lösung führte:
Mit anderen Worten, die obige Lösung ist praktisch, wenn Sie nichts dagegen haben:
Vorteile
Nachteile
Leider ist es immer noch besser als dies zu tun,
Für jede einzelne Eigenschaft, die mit der zusätzlichen Ausführlichkeit zum Albtraum wird ;-(
Hinweis: Ich behaupte nicht, dass diese Lösung im Vergleich zu den anderen leistungsmäßig besser ist, nur dass sie eine praktikable Lösung für diejenigen ist, die die anderen vorgestellten Lösungen nicht mögen.
quelle
Ich habe mir diese Basisklasse ausgedacht, um das beobachtbare Muster zu implementieren. Sie macht so ziemlich das, was Sie brauchen ( "automatisch" das Set implementieren und abrufen). Ich habe eine Stunde als Prototyp damit verbracht, daher gibt es nicht viele Unit-Tests, aber es beweist das Konzept. Beachten Sie, dass das verwendet wird
Dictionary<string, ObservablePropertyContext>
, um die Notwendigkeit für private Felder zu beseitigen.Hier ist die Verwendung
quelle
Ich schlage vor, ReactiveProperty zu verwenden. Dies ist die kürzeste Methode außer Fody.
stattdessen
( DOCS )
quelle
Eine andere Idee...
quelle
=> hier meine Lösung mit folgenden Funktionen
quelle
Benutze das
}}
quelle