I am sick to death of writing INPC setters like this:
public string Label
{
get {return _label;}
set
{
if (_label == value) return;
_label = value;
NotifyPropertyChanged(() => Label);
}
}
I'd like to refactor the setting of the field the same way I did INPC; I'd like to pass in the Expression<Func<T>>
(and probably the backing field) into something like this:
public string Label
{
get {return _label;}
set
{
SetProperty(() => Label, ref _label, value);
}
}
... and here's the implementation I've come up with in the base class:
public virtual void SetProperty<T>(Expression<Func<T>> expression, ref T field, T value)
{
if (Equals(field, value)) return;
field = value;
NotifyPropertyChanged(expression);
}
... and it seems to work - it compiles, at least <grin />. My question is: am I missing something here? is passing by ref going to accomplish what I'm trying to do?
Yes, it will. You're still going to be a little sick of writing private backing fields and repetitive parts though:
you can't easily escape from that.
INPC works based solely on three things:
The value-storage is actually not so important. It's important so that the property itself actually behaves, but it's not important for INPC itself. INPC is only about name+eventraising.
Your approach is OK, I'm often tempted to do the same. Writing INPCs implementation is .. soo boring. Technically, what you "invented" (sorry, you're not the first one, I saw at least a dozen similar implementations:) ) isn't really not much of a difference, you just extracted some common code to a method. Not rocket science, but still few lines saved. There is a small "expense" of not being able to tighty control when/how the event is raised. But not much of a pain though, as you can always write manual event-rising and custom conditions when needed.
However, there are some things worth noting:
if (Equals(field, value))
is a good start, but with custom objects it will may require you to overrideEquals
andGetHashCode
in many data classes, and it's not always a good idea, especially in WPF and bindings. When passing a Equals/HashCode-overriding dataobject to WPF you must take care and be prepared to see that sometimes the WPF will get confused and sometimes may not update bindings properly (i.e. it may fail to notice that an object was replaced with a new one and leave some bindings bounds to old instance). It's good to make an overload to SetProperty that will taking aIEqualityComparer<T>
instead; cosmetics though[CallerMemberName]
and friends and you will be able to remove the "string propertyName". It will cost a bit, but it may be handy for you.Caliburn Micro
and it'sPropertyChangedBase
class. You may find it very useful (Caliburn is available on nuget) or, at least, see the implementation. It uses above-mentioned caller information as default, and also has some nice features like boolean flag to temporarily disabling notifications (IsNotifying
). Very useful in some cases.PostSharp
utility, that is able to .. automatically generate all INPC implementations. You attach the PostSharp util as a postbuild step, and it reads attributes from your properties, and if it finds out that some property is marked as to-be-a-INPC, then it rewrites IL of your assembly and adds the INPC implementation to it. What's the gain?[INPC] public int Foo {get;set;}
and done. However, please note, PostSharp is commercial utility. You may be able to do such thing yourself if you find an IL weaver, probably based on Cecil from Mono. Or maybe you will find a free util for that too. I don't remember much more, sorry.EDIT:
Now you mentioned it - you quoted
SetProperty(()=> Label, somePocoClass.Label, value)
which uses a expressions to find the name of property in a compile-safe way (no strings! refactor/rename works). If you decide to stick to ref, then with some extra work, you can get the()=>Label
expression, analyze it and extract the name, then transform&rewrite that into val => _label = val delegate, and cache that delegate per-field, and then use that cached delegate instead using ref parameter, resulting in:set{ SetProperty(()=>Label, value); }
That's almost as "cool" as it could ever get wit current C# specs. But: Such SetProperty gets really complex, transforming expressions isn't light but can be made almost one-time cost, but delegate cache is not "for free" and you have to cache them, because calculating transformed delegates onthefly is way too heavy. I've tried that, I've seen others trying that, and from my observations, most of people quits. And many coworkers see it as magic and won't try to touch that if something works not right. Cool, fun, but you shouldn't need C# gurus for debugging/patching simple feature like INPC.
EDIT2:
Even simpler way - get some T4 templates to generate the code for you. This is also very popular way to handle that. Or rather, was, like 3-4 years ago. I know some people around who really love T4. Somehow, I not. I felt like writing PHP or first versions of ASP-not-Net.. but don't care about my opinion here - see them and try them for yourself. Many people like it.
By the way, sorry for being chaotic - I've just remembered some bits about IL weaving:
for starters, see this article, very informative, covers all the basics plus more - dynamic proxies, il weaving by Cecil, (...)
the "free PostSharp-like ILweaver" - it's called Fody, on GitHub. See for example this article on INPC auto-implementation via Fody