Microsoft powinien był zaimplementować coś nieoczekiwanego INotifyPropertyChanged
, na przykład we właściwościach automatycznych, po prostu sprecyzuj {get; set; notify;}
, że to ma sens, aby to zrobić. A może są jakieś komplikacje?
Czy sami możemy wdrożyć coś takiego jak „powiadomić” w naszych nieruchomościach? Czy istnieje wdzięczne rozwiązanie do wdrożenia INotifyPropertyChanged
w twojej klasie, czy jedynym sposobem na to jest podniesienie PropertyChanged
zdarzenia w każdej właściwości.
Jeśli nie, czy możemy napisać coś do automatycznego wygenerowania fragmentu kodu w celu wywołania PropertyChanged
zdarzenia?
Odpowiedzi:
Bez użycia czegoś takiego jak postsharp, minimalna wersja, której używam, używa czegoś takiego:
Każda właściwość jest wtedy po prostu czymś w rodzaju:
co nie jest ogromne; jeśli chcesz, można go również wykorzystać jako klasę podstawową.
bool
Powrót zSetField
powie Ci, czy to nie-op, w przypadku, gdy chcesz zastosować inną logikę.lub jeszcze łatwiej dzięki C # 5:
które można nazwać tak:
z którym kompilator doda
"Name"
automatycznie.C # 6.0 ułatwia implementację:
... a teraz z C # 7:
źródło
[CallerMemberName]
Począwszy od .Net 4.5 jest wreszcie łatwy sposób, aby to zrobić.
.Net 4.5 wprowadza nowe atrybuty informacji o dzwoniącym.
Prawdopodobnie dobrym pomysłem jest również dodanie funkcji porównującej.
Więcej przykładów tutaj i tutaj
Zobacz także informacje o dzwoniącym (C # i Visual Basic)
źródło
Naprawdę podoba mi się rozwiązanie Marca, ale myślę, że można go nieco ulepszyć, aby uniknąć użycia „magicznego ciągu” (który nie obsługuje refaktoryzacji). Zamiast używać nazwy właściwości jako łańcucha, łatwo jest uczynić ją wyrażeniem lambda:
Wystarczy dodać następujące metody do kodu Marca, to załatwi sprawę:
BTW, to zostało zainspirowane
tym blogiemzaktualizowanym adresem URLźródło
Istnieje również Fody, który ma dodatek PropertyChanged , który pozwala napisać to:
... a podczas kompilacji wstrzykuje powiadomienia o zmianie właściwości.
źródło
"Fody/.*?:",LogCustom2,True
wyróżnia go kolorem „Niestandardowy 2”. Zrobiłem go jasno różowym, więc łatwo go znaleźć. Po prostu wszystko Fody, to najfajniejszy sposób na robienie czegokolwiek, co ma wiele powtarzalnych pisania.Myślę, że ludzie powinni zwracać nieco większą uwagę na wydajność; to naprawdę wpływa na interfejs użytkownika, gdy trzeba związać wiele obiektów (pomyśl o siatce z ponad 10 000 wierszy) lub jeśli wartość obiektu zmienia się często (aplikacja do monitorowania w czasie rzeczywistym).
Wziąłem różne implementacje znalezione tutaj i gdzie indziej i zrobiłem porównanie; sprawdź porównanie wydajności implementacji INotifyPropertyChanged .
Oto rzut oka na wynik
źródło
Wprowadzam klasę Bindable na moim blogu pod adresem http://timoch.com/blog/2013/08/annoyed-with-inotifypropertychange/ Bindable używa słownika jako torby właściwości. Łatwo jest dodać niezbędne przeciążenia, aby podklasa zarządzała własnym polem zaplecza za pomocą parametrów ref.
Kod:
Można go użyć w następujący sposób:
źródło
protected T Get<T>(T defaultValue, [CallerMemberName] string name = null)
a także sprawdzićif (_properties.ContainsKey(name) && Equals(value, Get<T>(default(T), name)))
w Ustawie (aby podnieść i zapisać, gdy pierwszy raz ustawię na wartość domyślną)Właściwie to nie miałem okazji tego spróbować, ale następnym razem konfiguruję projekt z dużym wymaganiem INotifyPropertyChanged Mam zamiar napisać atrybut Postsharp , który wstrzyknie kod w czasie kompilacji. Coś jak:
Stanie się:
Nie jestem pewien, czy to zadziała w praktyce i muszę usiąść i wypróbować, ale nie rozumiem, dlaczego nie. Może być konieczne, aby zaakceptował niektóre parametry w sytuacjach, w których trzeba uruchomić więcej niż jeden OnPropertyChanged (jeśli na przykład miałem właściwość FullName w powyższej klasie)
Obecnie używam niestandardowego szablonu w Resharper, ale mimo to mam dość wszystkich moich właściwości, które są tak długie.
Ach, szybkie wyszukiwanie w Google (które powinienem był zrobić, zanim to napisałem) pokazuje, że przynajmniej jedna osoba zrobiła coś takiego wcześniej tutaj . Nie do końca to, co miałem na myśli, ale wystarczająco blisko, aby pokazać, że teoria jest dobra.
źródło
Tak, z pewnością istnieje lepszy sposób. Oto on:
Samouczek krok po kroku zmniejszył się w oparciu o ten przydatny artykuł .
NotifierInterceptor
ProxyCreator
-
Umieść wiązania w xaml:
Umieść wiersz kodu w pliku za kodem MainWindow.xaml.cs w następujący sposób:
DataContext = ProxyCreator.MakeINotifyPropertyChanged<MainViewModel>();
Uwaga!!! Wszystkie ograniczone właściwości powinny być ozdobione słowem kluczowym virtual, ponieważ były używane przez proxy proxy do przesłonięcia.
źródło
type
,interfaces to apply
,interceptors
.CreateClassProxy<T>
metody. Zupełnie inaczej ... hmmm, zastanawiam się, dlaczego tak ograniczone ogólną metodą. :(Podejście bardzo podobne do AOP polega na wstrzykiwaniu rzeczy INotifyPropertyChanged do już utworzonego obiektu w locie. Możesz to zrobić za pomocą czegoś takiego jak Castle DynamicProxy. Oto artykuł wyjaśniający technikę:
Dodanie INotifyPropertyChanged do istniejącego obiektu
źródło
Popatrz tutaj : http://dotnet-forum.de/blogs/thearchitect/archive/2012/11/01/die-optimale-implementierung-des-inotifypropertychanged-interfaces.aspx
Jest napisany w języku niemieckim, ale możesz pobrać ViewModelBase.cs. Wszystkie komentarze w pliku cs są napisane w języku angielskim.
Za pomocą tej klasy ViewModelBase można zaimplementować właściwości wiążące podobne do dobrze znanych właściwości zależności:
źródło
Na podstawie odpowiedzi Thomasa, która została zaadaptowana z odpowiedzi Marca, zmieniłem kod właściwości odbijającej w klasę podstawową:
Sposób użycia jest taki sam jak odpowiedź Thomasa, z tą różnicą, że możesz przekazać dodatkowe właściwości, o których chcesz powiadomić. Było to konieczne do obsługi kolumn obliczeniowych, które należy odświeżyć w siatce.
Mam ten element sterujący kolekcją elementów przechowywanych w BindingList odsłoniętym przez DataGridView. Eliminuje to konieczność ręcznego wywoływania funkcji Refresh () do siatki.
źródło
Pozwól, że przedstawię własne podejście zwane Yappi . Należy do generatorów klas pochodnych Runtime proxy, dodając nowe funkcje do istniejącego obiektu lub typu, takich jak dynamiczny serwer proxy Caste Project.
Pozwala zaimplementować INotifyPropertyChanged raz w klasie bazowej, a następnie zadeklarować klasy pochodne w następującym stylu, nadal obsługując INotifyPropertyChanged dla nowych właściwości:
Złożoność pochodnej konstrukcji klasy lub proxy można ukryć za następującą linią:
Wszystkie prace związane z implementacją INotifyPropertyChanged można wykonać w następujący sposób:
Jest w pełni bezpieczny do refaktoryzacji, nie wymaga odbicia po konstrukcji typu i jest wystarczająco szybki.
źródło
TDeclaration
parametru type naPropertyImplementation
? Z pewnością możesz znaleźć odpowiedni typ, z którego można wywoływać (a nie callvirt) tylko getter / setterTImplementation
?Wszystkie te odpowiedzi są bardzo miłe.
Moje rozwiązanie wykorzystuje fragmenty kodu do wykonania zadania.
Wykorzystuje to najprostsze wywołanie zdarzenia PropertyChanged.
Zapisz ten fragment i użyj go, gdy używasz fragmentu „fullprop”.
Możesz zmodyfikować połączenie według własnego uznania (aby skorzystać z powyższych rozwiązań)
źródło
Jeśli używasz dynamiki w .NET 4.5, nie musisz się martwić
INotifyPropertyChanged
.jeśli Nazwa jest powiązana z jakąś formantem, działa po prostu dobrze.
źródło
Innym połączonym rozwiązaniem jest użycie StackFrame:
Stosowanie:
źródło
get_Foo
metodę w trybie zwolnienia.W mojej podstawowej bibliotece utworzyłem metodę rozszerzenia do ponownego użycia:
Działa to z .Net 4.5 z powodu CallerMemberNameAttribute . Jeśli chcesz go używać z wcześniejszą wersją .Net, musisz zmienić deklarację metody z:
...,[CallerMemberName] string propertyName = "", ...
na...,string propertyName, ...
Stosowanie:
źródło
Rozwiązałem w ten sposób (jest to trochę pracochłonne, ale na pewno jest szybsze w środowisku wykonawczym).
W VB (przepraszam, ale myślę, że nie jest trudno przetłumaczyć to w C #), robię to podstawienie RE:
z:
To transofrm cały kod w ten sposób:
W
A jeśli chcę mieć bardziej czytelny kod, mogę być odwrotny, dokonując tylko następujących zmian:
Z
Rzucam, by zastąpić kod IL ustawionej metody, ale nie mogę napisać dużo skompilowanego kodu w IL ... Jeśli pewnego dnia go napiszę, powiem ci!
źródło
Trzymam to jako fragment. C # 6 dodaje ładną składnię do wywoływania programu obsługi.
źródło
Oto Unity3D lub wersja NotifyPropertyChanged dla wersji innej niż CallerMemberName
Ten kod umożliwia pisanie takich pól:
Ponadto w resharper, jeśli utworzysz fragment wzorca / wyszukiwania, możesz także zautomatyzować przepływ pracy, przekształcając proste pola prop w powyższy podkład.
Wzór wyszukiwania:
Zamień wzór:
źródło
Napisałem artykuł, który pomaga w tym ( https://msdn.microsoft.com/magazine/mt736453 ). Możesz użyć pakietu SolSoft.DataBinding NuGet. Następnie możesz napisać taki kod:
Korzyści:
źródło
Chociaż istnieje oczywiście wiele sposobów, aby to zrobić, z wyjątkiem magicznych odpowiedzi AOP, żadna z tych odpowiedzi nie wydaje się patrzeć na ustawienie właściwości Modelu bezpośrednio z modelu widoku bez posiadania lokalnego pola odniesienia.
Problem polega na tym, że nie można odwoływać się do nieruchomości. Można jednak użyć akcji, aby ustawić tę właściwość.
Można to wykorzystać jak następujący fragment kodu.
Sprawdź to repozytorium BitBucket, aby uzyskać pełną implementację metody i kilka różnych sposobów osiągnięcia tego samego wyniku, w tym metodę wykorzystującą LINQ i metodę wykorzystującą odbicie. Należy pamiętać, że te metody są wolniejsze pod względem wydajności.
źródło
Inną rzeczą, którą warto rozważyć przy wdrażaniu tego rodzaju właściwości, jest fakt, że oba INotifyPropertyChang * ed * używają klas argumentów zdarzeń.
Jeśli ustawiana jest duża liczba właściwości, liczba instancji klasy argumentu zdarzenia może być ogromna, należy rozważyć buforowanie ich, ponieważ są one jednym z obszarów, w których może wystąpić eksplozja łańcucha.
Spójrz na to wdrożenie i wyjaśnienie, dlaczego zostało pomyślane.
Blog Josh Smiths
źródło
Właśnie znalazłem ActiveSharp - Automatic INotifyPropertyChanged , jeszcze go nie użyłem, ale wygląda dobrze.
Cytowanie z jego strony internetowej ...
Zamiast tego napisz właściwości takie jak to:
Zauważ, że nie trzeba dołączać nazwy właściwości jako łańcucha. ActiveSharp niezawodnie i poprawnie rozpoznaje to samo. Działa w oparciu o fakt, że implementacja właściwości przekazuje pole zaplecza (_foo) przez ref. (ActiveSharp używa tego wywołania „przez ref”, aby zidentyfikować, które pole zaplecza zostało przekazane, a na podstawie pola identyfikuje właściwość).
źródło
Pomysł wykorzystujący refleksję:
źródło
Zdaję sobie sprawę, że na to pytanie ma już gazillion odpowiedzi, ale żadne z nich nie było dla mnie odpowiednie. Mój problem polega na tym, że nie chcę żadnych hitów wydajnościowych i jestem gotów pogodzić się z małą gadatliwością tylko z tego powodu. Nie dbam też zbytnio o właściwości auto, co doprowadziło mnie do następującego rozwiązania:
Innymi słowy, powyższe rozwiązanie jest wygodne, jeśli nie masz nic przeciwko:
Plusy
Cons
Niestety, nadal jest to lepsze niż robienie tego,
Dla każdej pojedynczej nieruchomości, która staje się koszmarem z dodatkową gadatliwością ;-(
Uwaga: nie twierdzę, że to rozwiązanie jest lepsze pod względem wydajności niż inne, tylko że jest to realne rozwiązanie dla tych, którzy nie lubią innych przedstawionych rozwiązań.
źródło
Wymyśliłem tę klasę bazową, aby zaimplementować możliwy do zaobserwowania wzorzec, właściwie robi to, czego potrzebujesz ( „automatycznie” wdrażając zestaw i otrzymaj). Spędziłem na tym godzinę jako prototyp, więc nie ma wielu testów jednostkowych, ale potwierdza tę koncepcję. Zauważ, że wykorzystuje to,
Dictionary<string, ObservablePropertyContext>
aby usunąć potrzebę prywatnych pól.Oto użycie
źródło
Proponuję użyć ReactiveProperty. To najkrótsza metoda oprócz Fody.
zamiast
( DOCS )
źródło
Inny pomysł...
źródło
=> tutaj moje rozwiązanie z następującymi funkcjami
źródło
Użyj tego
}
źródło