In a C# application, I've got a behaviour that I would like to be available in different classes that not necessarily share the same ancestor. What better opportunity to 'favour composition over inheritance?"
In this case not only composition is a better way, but can also be the only way.
Unfortunately, the code is the following. I left only the relevant part, obviously it would make little sense to delegate only this, but this is the problematic part.
public void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
Turns out that since PropertyChanged is an event, it can only be raised inside the declaring class.
I evaluated all the proposed alternatives to this, found in this S&O question: Alternatives and all of them seems a bit of a hack. That is, events are not meant to be delegated. Fundamentally, I would like that someone proof me to be wrong on this point.
Unfortunately this seems to be another case of the pattern: you use inheritance? no no no, there is a better approach. And when you try to implement it you suddenly bump into problems that require various levels of hacking the language or bringing complexity of the solution to an whole other level.
At the end of the day inheritance or composition is not a marriage, so I don't want to stay with either of them for life; I considered myself to be married with the non-duplication, or DRY principle. So I wanted to find a good way of not repeating the code. Here I simply cannot find a reasonably-simple solution and hope that someone else that has already bumped into this problem proposed some perhaps 'third way' solution.
1 Answer 1
There are certain things that do require inheritance. In absence of other language features, this example would be one of them. Note that the phrase is to favor composition over inheritance. That does not mean throw out inheritance where it is warranted.
However, C# specifically does provide a nice out here. That's the use of Extension Methods. Since INotifyPropertyChanged
declares the event and the identifies the behavioral pattern, you can implement the specific feature you are talking about using this feature:
public static class FrameworkExtensions
{
public static void NotifyPropertyChanged(this INotifyPropertyChanged model,
[CallerMemberName] string property = "")
{
model.PropertyChanged?.Invoke(model, new PropertyChangedEventArgs(property));
}
}
The important thing to make it a reality is that the user needs to import the namespace this is declared in. If there are other framework classes that are commonly used in code, that is probably done for you.
-
-
2It still does not compile.
error CS4022: The CallerMemberNameAttribute may only be applied to parameters with default values
. Fix that and you geterror CS0079: The event 'INotifyPropertyChanged.PropertyChanged' can only appear on the left hand side of += or -=
anderror CS7036: There is no argument given that corresponds to the required formal parameter 'e' of 'PropertyChangedEventHandler.Invoke(object, PropertyChangedEventArgs)'
Sean Reid– Sean Reid03/02/2020 15:37:01Commented Mar 2, 2020 at 15:37 -
Doesn't work with either .NET 4.6 and .NET 4.7. Same error "The event 'INotifyPropertyChanged.PropertyChanged' can only appear on the left hand side of += or -=..."AgostinoX– AgostinoX03/02/2020 16:03:19Commented Mar 2, 2020 at 16:03
-
3@helb, C# 6 was released five years ago. If someone is still using an even older version of the compiler, then needing to use inheritance for this task is the least of their problems.David Arno– David Arno03/02/2020 17:07:43Commented Mar 2, 2020 at 17:07
-
1
Fody.PropertyChanged
on all my classes that require anINotifyPropertyChanged
implementation. Problem solved, no inheritance required.