-
Notifications
You must be signed in to change notification settings - Fork 1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Proposal: Make the NotifyChanged event part of each object #1419
Comments
Doing this automatically for all objects would incur a major performance penalty when it isn't needed. Using an attribute would be covered by code generation (#107). |
Maybe I should add some example code to explain how you could handle this
|
@basoundr : From what I understand, CodeGenerator would make it easier to write a standard INotifyPropertyChanged implementation, but that would be just cosmetic.
|
@Starwer How does your proposal remove those limitations? |
In the meantime, this comment makes the case that there's a way to make life much easier in those objects which must implement or inherit from a class implementing INotifyPropertyChanged: #140 (comment) |
@svick: As explained, the proposal is not to add just to add a some cosmetic On the following for example, it is quite obvious, at design time, for the Compiler that the collection is being changed:
Think about the amount of code you need to do that with INotifyPropertyChanged, and how it will (not) be optimized in that case. Now if you have an call like that:
Pretty obvious for the compiler that groupA content changed... Imagine the gears required in INotifyPropertyChanged and all the LINQ involved. Think also about applications like for debugging, if you could say: Track any change in groupA (wherever it is happening in the code, whatever sub-child it could be). That's the same idea. It just strikes me that such a critical feature in VMMV is not natively handled by the compiler. |
Sure, but my question is: how do you handle the non-obvious cases? How exactly does the compiler track the "dependency graph", across multiple methods and even assemblies? Consider e.g.: void ChangeAFriend(ref string friend)
{
friend = "Jane";
}
ChangeAFriend(ref friends[1]); Or: departments[0].NotifyChanged += FirstDepartmentChanged;
foreach (var employee in allEmployees)
{
// how does the compiler know which employees belong to departments[0]?
employee.Salary *= 1.05m;
} |
This might help you. |
@svick : Ok, now we start talking... First of all, let me introduce myself: I spent 2 years full time in developing a C Compiler in Philips (although it was in 2004-6... I'm not getting younger...). So I do know it is a challenging thing. We make it easy for the user, but difficult for the compiler (but that's what a compiler is for ... IMHO). Precisely, I do ask it because it is difficult, and that's precisely the reason why this feature would make a difference on C#. This is where it can truly differentiate from C++ or Java... another way than in syntactical sweeteners. An universal change/event, that only exists in C, Tcl ... debuggers. For your first example:
We handle that the same way we handle polymorphism inheriting implicitly from an Observable class. If we know that friends[] is Observable. This class has in a sense an implicit = operator
But wait ! We can not inherit from string ! Exact ! That's why we need the compiler to handle that, and to relax this restriction behind the scene. All the fancy libraries that generate some INotifyPropertyChanged can't do that. Same for the second example. |
Note: I have now reached the 4x 👎 and a confused emoji...
I'm willing to take a lesson on this total failure.... but from the contradictory comments I can't really get the take-away message... |
As I see it, there are two ways to achieve your proposed idea. #1 Implement the existing This would have a significant negative performance impact on a very large amount of code. While there are many projects that use WPF style databinding where this would be useful, there are even more where Doing this instead by opt-in on specific objects is already easily done, whether by hand coding, aspect-oriented programming (e.g. PostSharp), code generation or other approaches. So, this is trivial. #2 Enhance the compiler to automagically modify just the right properties when needed While the data flow analysis for this might (might!) be possible for trivial projects, I suggest its impossible for even moderately complex situations. It seems you're wanting the compiler to consider every use of a class/property throughout the entire project when deciding how to compile an individual property. Issues that arise from this ...
FWIW, these examples stem from code I've worked on just this week. This isn't just complicated, it's impossible. There are existing proposals that would make correct implementation of |
@Starwer ❤️ It can be counter-instinctive to see your idea being downvoted without taking it personally, so I very much respect the constructive approach you are taking. |
Agreed with @jnm2. FWIW I feel there is merit in language features built around push semantics. Keeping multiple objects in sync when changes are made to any of them is a clunky process, and I welcome proposals that can make this significantly better. |
There are some generators for this purpose: https://www.nuget.org/packages/ValueChangedGanerator/ |
@Starwer something like Kotlin's delegated properties (with an option to delegate all properties of a type) would be a more controllable mechanism for what you're trying to achieve. |
Thanks @theunrepentantgeek . I really value the time and effort (even more than a hypothetical thumb up) you spent to explain your view and show that I going too far into the concept. I indeed didn't come up with all the solution and the implementation, but wanted to initiate an concept. So, this is my last shot. I'd really like to know (please!) if you're all against the very concept of this change, regardless of the implementation itself (the "What/why", before digging on the "how"): What: (Specifications)
Why:
How:
I believe there is something between the "trivial" and the "impossible" approach... to be discussed... if you're not already against the "What". I could think that when somebody came up with the idea of enabling any method to be asynchronous automagically, he/she had the very similar objections:
But eventually, they extended the concept with the Task/async/await combo, and for me there's no doubt now that this is a real improvement for the C# language. I wanted to know if you were open for a similar discussion here. |
@Starwer Not the case at all. If I thought it was bad, I would have given it a thumbs down. I just don't feel the proposal is very compelling in its current form. |
I'm not seeing how much of this can be implemented. The C# compiler doesn't have that level of control over what it compiles. It certainly can't, say, "lift restrictions" around I do understand that observing object changes is fairly important, particularly in the WinForm/WPF world. I could see enabling the compiler to automatically generate the boilerplate to invoke that event when a class is adorned with something like |
I'm surprised nobody mentionned Fody.PropertyChanged yet (https://github.com/Fody/PropertyChanged) It works a lot like @Starwer's original idea: [AddINotifyPropertyChangedInterface]
public class Person
{
public string GivenNames { get; set; }
public string FamilyName { get; set; }
[DependsOn(nameof(GivenName), nameof(FamilyName))]
public string FullName => $"{GivenNames} {FamilyName}";
} |
@Logerfo, @ufcpp, @orthoxerox, @sylveon : I pretty much appreciate the effort put into all the libraries and code generators to simplify the use of INotifyPropertyChanged. However, the fact that there are so many different solutions to solve the same problem flourishing on the WEB is for me a sign that there is a hole to fill in the language itself. Also, this proves one thing: if a code-generator can do it, a compiler can also do it. @HaloFour, @bondsbw, @theunrepentantgeek, @svick: the observations/reluctances you presented challenged me to deepen the concept. A more mature proposal starts to shape in my mind (similar to async/await model in fact). However, I'm afraid I've burnt down all my credibility on that topic. But if you think I should continue and come back we a more thorough plan (but less open for discussion de facto), I'd be glad to spend the required hours and come back with a solid proposal. |
Part of the mantra behind the source generators proposal is that if it can be accomplished relatively easily through annotations and auto-generated boilerplate source that it should be done that way rather than impacting the language proper. This is to both enable the community to iterate on ideas like this and ship product much faster than waiting on the compiler team to get around to implementing it directly in the compiler. Language changes are incredibly expensive in terms of time and resources and many good ideas have been sitting in a holding pattern since Roslyn first went open-source back in 2014. That said, source generators themselves are quite expensive and have proven difficult to implement due to the tooling considerations, so it's hard to say whether deferring to them is really that appropriate.
I think the comparison to |
The discussion here is if the compiler should do it. |
I think that most people here will agree that the current situation with For me, for a solution to be considered, it has to satisfy the following points:
|
@svick : Thanks for you constructive post. Even though it seems contradictory to what I proposed, I acknowledge all you wrote. And I ensure you that I perfectly understand the difference between the relationship between the compiler, the CLR, the standard library, a code-generator and so on... @HaloFour : I could only disagree with you on that thing: IMHO the comparison await/async is quite relevant. But on the others things you stated, I agree on every word. There is indeed a steep hill to climb, and I have the impression I will have to climb it alone... which I just can't. Also, I didn't realize that people seem pretty satisfied about using I was expecting enthusiasm about this proposal, but got exactly the opposite (the only encouragement I got is when I stated this proposal was a fail). I'll sleep on it, but I think I'll just give up the fight. Don't get me wrong, despite my disappointment I really appreciated your dedication to drive the discussion in the right direction (to all). |
@Starwer Your motivations are in the right place and I'd encourage you to submit further proposals when you have them. Submitting your ideas to the scrutiny of critics is never easy, but it is worthwhile. I've made a couple of proposals for C# features that I thought were really good ideas - one (#589) was met by a resounding meh and the other by disagreement (#1332). The threshold for an idea to make it into the language is incredibly high - and that's a good thing because it means that those language features will be genuinely and widely useful. As to your specific proposal … I can think of ways (perhaps based on your However, not all objects need an implementation of And here we run into a paradox. If developers don't know how to correctly implement In my opinion, automated approaches are a great idea, as long as they satisfy one of two conditions. Either they solve exactly 100% of the problem, or they provide override/injection/configuration options to allow the edge cases to be correctly handled. The async/await feature does exactly this. |
This is interesting, apparently the VB.NET team has a bit of interest in this problem: Maybe you're using the wrong language? 😉 |
@HaloFour that looks very much like Kotlin's delegated properties. Decorated properties? |
@Logerfo : Thanks for this exemplary truism. @theunrepentantgeek : Thanks for your encouragements and (again) the effort spent to elaborate on the topic. @HaloFour: Very interesting link. It indeed seems I'm in the wrong language. That's unfortunate that I dislike VB syntax in the first place. That's purely a matter of personal preference BTW, I don't mean VB is bad or ugly by nature... but I've quit the Basic languages in the 90's (Amiga) and there's no way I'm going back there. You (all) convinced me that the idea was bad and/or premature. I'll then close this issue as it goes nowhere the way it was started. I'll maybe come back one day with a proper proposal on a built-in Observer Design Pattern, if I'm disappointed about the universal #107 (you triggered a very high expectation there). |
In my view, C#/WPF is the magic combo promoted by Microsoft. These are designed to work hand in hand and should support each-other optimally in a MVVM world. And I must say I'm pretty convinced by this association.
Now, whereas the learning in C# is a snap for an experienced programmer, I feel that learning WPF is quite a long way interrupted with road-blocks and paved with frustration. The main reason: The Binding can be done only on class properties that implement INotifyPropertyChanged interface. I think the experienced WPF developers are smiling now thinking: "Of course ! This is an easy one !"
But I think every newcomer in WPF has faced this problem, where he was trying to bind a non-observable property, or not even a property, or binding an ObservableCollection wishfully thinking that the changes on items on the collection would be detected... But also admit it, how much time did you spend to solve, implement or work-around this limitation or draw fancy dependency graphs to bind your properties correctly ?
This could have been avoided by having the change event implemented differently. Instead of having it patched over to the .NET library with the quick Implementation of the INotifyPropertyChanged interface, it could be handled by the compiler.
Think of an
NotifyChanged
event on theobject
base class, that would be handled directly by the C# Compiler... All the restriction to bind objects only if these are properties from an instance of object implementing an INotifyPropertyChanged would disappear, your Model could become your ViewModel, the binding would become simpler... and the performance would be way better: Less code size, faster response !If this is too complicated for the compiler to figure out where the
NotifyChanged
should be added to the IL, we may consider to add a compiler attribute like[Observable]
Think of this as a new ViewModel:
That you can simply bind with:
Wouldn't that make your day ?
The text was updated successfully, but these errors were encountered: