Without inherit but only with reflection is it possible to dynamically change the code of a method in C#?
something like :
nameSpaceA.Foo.method1 = aDelegate;
I cannot change/edit The Foo Class.
namespace nameSpaceA
{
class Foo
{
private void method1()
{
// ... some Code
}
}
}
My final objective is to change dynamicaly the code of :
public static IList<XPathNavigator> EnsureNodeSet(IList<XPathItem> listItems);
In System.Xml.Xsl.Runtime.XslConvert.cs
to turn :
if (!item.IsNode)
throw new XslTransformException(Res.XPath_NodeSetExpected, string.Empty);
into :
if (!item.IsNode)
throw new XslTransformException(Res.XPath_NodeSetExpected, item.value);
The first part of this answer is wrong, I'm only leaving it so that the evolution in the comments makes sense. Please see the EDIT(s).
You're not looking for reflection, but emission (which is the other way around).
In particular, there's a method that does just what you want, lucky you!
See TypeBuilder.DefineMethodOverride
EDIT:
Writing this answer, I just remembered that re-mix allows you to do this too. It's way harder though.
Re-mix is a framework that "simulates" mixins in C#. In its basic aspect, you can think of it as interfaces with default implementations. If you go further, it becomes much more than that.
EDIT 2: Here is an example of use for re-mix (implementing INotifyPropertyChanged on a class that doesn't support it, and has no idea of mixins).
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Remotion.Mixins;
using System.ComponentModel;
using MixinTest;
[assembly: Mix(typeof(INPCTester), typeof(INotifyPropertyChangedMixin))]
namespace MixinTest
{
//[Remotion.Mixins.CompleteInterface(typeof(INPCTester))]
public interface ICustomINPC : INotifyPropertyChanged
{
void RaisePropertyChanged(string prop);
}
//[Extends(typeof(INPCTester))]
public class INotifyPropertyChangedMixin : Mixin<object>, ICustomINPC
{
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string prop)
{
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(prop));
}
}
}
public class ImplementsINPCAttribute : UsesAttribute
{
public ImplementsINPCAttribute()
: base(typeof(INotifyPropertyChangedMixin))
{
}
}
//[ImplementsINPC]
public class INPCTester
{
private string m_Name;
public string Name
{
get { return m_Name; }
set
{
if (m_Name != value)
{
m_Name = value;
((ICustomINPC)this).RaisePropertyChanged("Name");
}
}
}
}
public class INPCTestWithoutMixin : ICustomINPC
{
private string m_Name;
public string Name
{
get { return m_Name; }
set
{
if (m_Name != value)
{
m_Name = value;
this.RaisePropertyChanged("Name");
}
}
}
public void RaisePropertyChanged(string prop)
{
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(prop));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
}
And the test:
static void INPCImplementation()
{
Console.WriteLine("INPC implementation and usage");
var inpc = ObjectFactory.Create<INPCTester>(ParamList.Empty);
Console.WriteLine("The resulting object is castable as INPC: " + (inpc is INotifyPropertyChanged));
((INotifyPropertyChanged)inpc).PropertyChanged += inpc_PropertyChanged;
inpc.Name = "New name!";
((INotifyPropertyChanged)inpc).PropertyChanged -= inpc_PropertyChanged;
Console.WriteLine();
}
static void inpc_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
Console.WriteLine("Hello, world! Property's name: " + e.PropertyName);
}
//OUTPUT:
//INPC implementation and usage
//The resulting object is castable as INPC: True
//Hello, world! Property's name: Name
Please note that:
[assembly: Mix(typeof(INPCTester), typeof(INotifyPropertyChangedMixin))]
and
[Extends(typeof(INPCTester))] //commented out in my example
and
[ImplementsINPC] //commented out in my example
Have the exact same effect. It is a matter of where you wish to define that a particular mixin is applied to a particular class.
Example 2: overriding Equals and GetHashCode
public class EquatableByValuesMixin<[BindToTargetType]T> : Mixin<T>, IEquatable<T> where T : class
{
private static readonly FieldInfo[] m_TargetFields = typeof(T).GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
bool IEquatable<T>.Equals(T other)
{
if (other == null)
return false;
if (Target.GetType() != other.GetType())
return false;
for (int i = 0; i < m_TargetFields.Length; i++)
{
object thisFieldValue = m_TargetFields[i].GetValue(Target);
object otherFieldValue = m_TargetFields[i].GetValue(other);
if (!Equals(thisFieldValue, otherFieldValue))
return false;
}
return true;
}
[OverrideTarget]
public new bool Equals(object other)
{
return ((IEquatable<T>)this).Equals(other as T);
}
[OverrideTarget]
public new int GetHashCode()
{
int i = 0;
foreach (FieldInfo f in m_TargetFields)
i ^= f.GetValue(Target).GetHashCode();
return i;
}
}
public class EquatableByValuesAttribute : UsesAttribute
{
public EquatableByValuesAttribute()
: base(typeof(EquatableByValuesMixin<>))
{
}
}
That example is my implementation of the hands-on lab given with re-mix. You can find more information there.
Related
I'm getting a null exception, but the field was initialized as an empty list. So how could it be null?
The error occurs on the second line in this method (on _hydratedProperties):
protected virtual void NotifyPropertyChanged<T>(Expression<Func<T>> expression)
{
string propertyName = GetPropertyName(expression);
if (!this._hydratedProperties.Contains(propertyName)) { this._hydratedProperties.Add(propertyName); }
}
And this is how the field is declared:
public abstract class EntityBase<TSubclass> : INotifyPropertyChanged where TSubclass : class
{
private List<string> _hydratedProperties = new List<string>();
This is how it's set:
public Eta Eta
{
get { return this._eta; }
set
{
this._eta = value;
NotifyPropertyChanged(() => this.Eta);
}
}
This is the full class (with the comments and non-relevant parts removed):
[DataContract]
public abstract class EntityBase<TSubclass> : INotifyPropertyChanged where TSubclass : class
{
private List<string> _hydratedProperties = new List<string>();
public bool IsPropertyHydrated(string propertyName)
{
return this._hydratedProperties.Contains(propertyName);
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void NotifyPropertyChanged<T>(Expression<Func<T>> expression)
{
string propertyName = GetPropertyName(expression);
if (!this._hydratedProperties.Contains(propertyName)) { this._hydratedProperties.Add(propertyName); }
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
public string GetPropertyName<T>(Expression<Func<T>> expression)
{
MemberExpression memberExpression = (MemberExpression)expression.Body;
return memberExpression.Member.Name;
}
}
Derived class:
[DataContract]
public class Bin : EntityBase<Bin>
{
private Eta _eta;
[DataMember]
public Eta Eta
{
get { return this._eta; }
set
{
this._eta = value;
NotifyPropertyChanged(() => this.Eta);
}
}
}
Here's the clue:
[DataContract]
Yup. DataContractSerializer does not call any constructor. Instead, it uses FormatterServices.GetUninitializedObject to create the object that will be deserialized. This bypasses the constructor call.
Your initializer:
private List<string> _hydratedProperties = new List<string>();
is translated to an implicit default constructor by the compiler.
As a workaround, you can use a deserialization callback with OnDeserializingAttribute:
[DataContract]
public abstract class EntityBase<TSubclass> : INotifyPropertyChanged
where TSubclass : class
{
private List<string> _hydratedProperties;
protected EntityBase()
{
Init();
}
private void Init()
{
_hydratedProperties = new List<string>()
}
[OnDeserializing]
private void OnDeserializing(StreamingContext context)
{
Init();
}
// ... rest of code here
}
I found a simpler answer than what Lucas provided. I'm not sure if this one is actually better, but it's simple and it worked. All I did was add the DataMember attribute to the field. Since that specifies that the field is part of the data contract, it is included with serialization/deserialization and no longer causes a null reference error.
[DataContract]
public abstract class EntityBase<TSubclass> : INotifyPropertyChanged where TSubclass : class
{
[DataMember]
private List<string> _hydratedProperties = new List<string>();
// More code here
}
I've been reading a few articles the past few days on inheriting classes and creating base classes, which i often do in tools I've written. However I specifically was looking into ways to reduce the redundant code often written in my classes which contain INotifyPropertyChange. Normally my classes look something like this, inheriting the base class of NotifyBase. However I've seen here a there in various scripts people moving some of the Get and Set code into the base class. I wanted to know what things to look out for when doing this? is this bad practice or good practice? Does the example I provided written correctly to do so?
One benefit being the Get and Set are much simpler in the classes which inherit NotifyBase in the new setup.
Current Setup Example
FileItem Class
using System.IO;
namespace WpfApplication1
{
public class FileItem : NotifyBase
{
private string _fullPath;
public string FullPath
{
get { return this._fullPath; }
set
{
this._fullPath = value;
this.NotifyPropertyChanged("FullPath");
}
}
}
}
Base Class 'NotifyBase'
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace WpfApplication1
{
public class NotifyBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
Possible New Setup
FileItem Class
using System.IO;
namespace WpfApplication1
{
public class FileItem : NotifyBase
{
public string FullPath
{
get { return Get<string>(); }
set { Set(value); }
}
}
}
Base Class 'NotifyBase'
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace Varo.Helper
{
public class NotifyBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
private readonly Dictionary<string, object> _propertyValues;
protected NotifyBase()
{
_propertyValues = new Dictionary<string, object>();
}
protected void Set<T>(T value, [CallerMemberName] string name = "")
{
if (_propertyValues.ContainsKey(name))
{
_propertyValues[name] = value;
NotifyPropertyChanged(name);
}
else
{
_propertyValues.Add(name, value);
NotifyPropertyChanged(name);
}
}
protected T Get<T>([CallerMemberName] string name = "")
{
if (_propertyValues.ContainsKey(name))
{
return (T)_propertyValues[name];
}
return default(T);
}
}
}
Check the PropertyChanged.Fody NuGet
Weaves .NET assembly at compile time so properties declared as
public int Property { get; set; }
Gets compiled as
private int property;
public int Property
{
get
{
return property;
}
set
{
if(property != value)
{
property = value;
if (this.PropertyChanged!= null) PropertyChanged(this, new PropertyChangedEventArgs("Property");
}
}
}
I have two classes that inherit from the same abstract class. I want both of them or at least one to be aware of changes in a specific property of the other. Is there any simple method for doing this? I've been trying to move the variable to the parent class, but that just creates 2 of the same variable, and when I create a reference to the other class inside the first one the same thing happens. thanks.
This is what my code looks like:
public abstract class Animal
{
public int MovementSpeed;
public bool Death;
public string Feedback;
public bool DeerCaught;
public int tiredRate;
public virtual int Movement()
{
MovementSpeed = MovementSpeed - tiredRate;
return MovementSpeed;
}
public virtual string Print()
{
return Feedback;
}
}
public class Deer : Animal
{
public string hidden;
public string Foraging;
public int DeerCount;
public Deer()
{
this.DeerCount = 10;
this.DeerCaught = false;
this.MovementSpeed = 10;
this.tiredRate = 2;
}
public void Hide()
{
if (Hunting)
{
Feedback = "The deer is hiding.";
if (DeerCount > 0)
{
Print();
}
}
else
{
//Forage();
}
}
public void Forage()
{
if (!Hunting)
{
Feedback = "The deer is searching for food.";
if (DeerCount > 0)
{
Print();
}
}
else
{
//Hide();
}
}
}
public class Wolf : Animal
{
public int Hunger;
public bool Hunting;
public Wolf()
{
this.Hunting = false;
this.Hunger = 10;
this.MovementSpeed = 10;
this.tiredRate = 1;
}
public bool Hunt()
{
if (Hunger < 5)
{
Hunting = true;
Feedback = "The wolf is searching for his next meal.";
if (DeerCaught == true)
{
Hunger++;
}
else
{
Hunger--;
}
return Hunting;
}
else
{
Hunting = false;
Feedback = "The wolf decides to rest.";
Hunger--;
return Hunting;
}
}
public void Die()
{
if (Hunger < 0)
{
Death = true;
Feedback = "The wolf has lost the hunt.";
}
}
}
I've tried setting Hunting as static in the base class, but I just end up getting two different versions of 'Hunting' when I run the methods of each class.
If this is intended as a simulation, then Deer isn't told when a wolf is hunting, it has to find out. The analogue here is to have some way that the Deer can query about the presence of wolves (something like Deer.LookForWolves(), then to check the value of the Hunting property on each wolf. This will require some sort of controller class, representing the world.
class World
{
public static List<Animal> Animals = new List<Animal>();
//...
}
class Deer : Animal
{
//...
bool IsSafe()
{
return LookForWolves().All(wolf => !wolf.Hunting);
}
List<Wolf> LookForWolves()
{
return World.Animals.OfType<Wolf>();
}
//...
Alternatively, you could reference World as a member of each Animal, passed in via the constructor. It's up to you, and will depend on whether you need to have multiple World objects, each with a different list of Animals.
Something like implementing INotifyPropertyChanged could help:
First, declare some classes that implement INotifyPropertyChanged:
abstract class Base {
}
class ClassA : Base, INotifyPropertyChanged {
public event PropertyChangedEventHandler PropertyChanged;
private string _property;
public string ClassAProperty {
get {
return _property;
}
set {
_property = value;
PropertyChanged(this, new PropertyChangedEventArgs("ClassAProperty"));
}
}
}
class ClassB : Base, INotifyPropertyChanged {
public event PropertyChangedEventHandler PropertyChanged;
private string _property;
public string ClassBProperty {
get {
return _property;
}
set {
_property = value;
PropertyChanged(this, new PropertyChangedEventArgs("ClassBProperty"));
}
}
}
Then, wire up new instances to subscribe to the PropertyChanged event:
using System.ComponentModel;
static void Main(string[] args) {
ClassA a = new ClassA();
a.PropertyChanged += PropertyChanged;
a.ClassAProperty = "Default value";
ClassB b = new ClassB();
b.PropertyChanged += PropertyChanged;
b.ClassBProperty = "Default value";
b.ClassBProperty = "new value in B";
a.ClassAProperty = "new value in A";
Console.Read();
}
static void PropertyChanged(object sender, PropertyChangedEventArgs e) {
Console.WriteLine("Property {0} on object {1} was changed, the value is \"{2}\"", e.PropertyName, sender.GetType().Name, sender.GetType().GetProperty(e.PropertyName).GetValue(sender));
}
Output of this is:
Property ClassAProperty on object ClassA was changed, the value is "Default value"
Property ClassBProperty on object ClassB was changed, the value is "Default value"
Property ClassBProperty on object ClassB was changed, the value is "new value in B"
Property ClassAProperty on object ClassA was changed, the value is "new value in A"
Each time either property is set, PropertyChanged is called, which in the above example writes the details to the console.
In your use case, you would have the event call a method in the other class (if I understand you correctly).
A very basic way to notify property changed with your own delegate definition. Since you do not provide any code I made up some classes myself. Use this as an example to modify your own code:
public delegate void PropertyChangedEventHandler();
public abstract class Base
{
}
public class A : Base
{
public event PropertyChangedEventHandler PropertyChanged;
private int _value;
public int Value
{
get { return _value; }
set
{
_value = value;
if (PropertyChanged != null)
{
PropertyChanged();
}
}
}
public class B : Base
{
private A _a;
public B(A a)
{
_a = a;
a.PropertyChanged += new PropertyChangedEventHandler(a_PropertyChanged);
}
private void a_PropertyChanged()
{
Console.WriteLine(_a.Value);
}
}
public class Application()
{
public void DoStuff()
{
var a = new A();
var b = new B(a);
}
}
The basic idea is to pass a reference of one object to the other. For example tell the deer it is being hunted by the wolf:
public class Wolf : Animal
{
public void Hunt(Deer deer)
{
deer.SetHunter(this);
}
}
Now the deer can check whether a wolf is hunting it:
public class Deer : Animal
{
Wolf _hunter;
public void SetHunter(Wolf wolf)
{
_hunter = wolf;
}
public void Hide()
{
if (_hunter != null)
{
Feedback = "The deer is hiding.";
}
else
{
//Forage();
}
}
}
This can be improved to be more generic, but it's the basic idea of passing a reference of one object to the other.
Don't use public fields for the properties of your classes. This way you will never be aware of changes and therefore can not notify others. Put the public fields into properties and always use these properties to change the value even from inside the Animal class. The property setter can then be used to notify others of changes.
public abstract class Animal
{
private int _movementSpeed;
public int MovementSpeed
{
get
{
return _movementSpeed;
}
set
{
if (_movementSpeed != value)
{
_movementSpeed = value;
OnMovementSpeedChanged();
}
}
}
protected virtual void OnMovementSpeedChanged()
{
// Derived classes can override this method.
// It will be called each time MovementSpeed changes.
}
public virtual int Movement()
{
// always use the property to change the value
// otherwise OnMovementSpeedChanged would never be called
MovementSpeed -= tiredRate;
return MovementSpeed;
}
}
Like others already mentioned you can also implement INotifyPropertyChanged in your base class. Since this uses events for notification not only derived classes can use that but also any other object that has a reference to an animal. The approach is basically the same. Each time the property value changes you call a method that fires the event. Any other object can then handle that event.
public abstract class Animal : INotifyPropertyChanged
{
private int _movementSpeed;
public int MovementSpeed
{
get
{
return _movementSpeed;
}
set
{
if (_movementSpeed != value)
{
_movementSpeed = value;
// call this method each time a property changes
OnPropertyChanged(new PropertyChangedEventArgs("MovementSpeed"));
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(PropertyChangedEventArgs args)
{
// always implement events like this
// -> check if the event handler is not null, then fire it
if (PropertyChanged != null)
{
PropertyChanged(this, args);
}
}
}
A class that wants to handle the event can do it like so:
public class AnyClass
{
public AnyClass(Animal anAnimal)
{
TheAnimal = anAnimal;
anAnimal += Animal_PropertyChanged;
}
public Animal TheAnimal { get; private set; }
private void Animal_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "MovementSpeed")
{
Console.WriteLine("MovementSpeed changed");
}
}
}
Derived classes however don't need to handle the event. Since the OnPropertyChanged method is declared as protected virtual they can just override it.
public class Deer : Animal
{
protected override void OnPropertyChanged(PropertyChangedEventArgs args)
{
if (args.PropertyName == "MovementSpeed")
{
Console.WriteLine("MovementSpeed changed");
}
// don't forget to call the base class otherwise the event will never get fired
base.OnPropertyChanged(args);
}
}
If I have the following layout:
public class A : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public B { get; set; }
}
public class B { public C { get; set; } }
public class C { public D { get; set; } }
public class D { public E { get; set; } }
//... add n classes
public class Z
{
public int Property
{
set
{
if(PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("Property"));
}
}
}
What is the cleanest way for me to notify A when A.B.C.D.E...Z.Property changes?
When anything inside of A changes, I want it to be flagged as "dirty" so I can tell the system that A needs to be saved.
I was actually working on this exact same problem just recently. My approach was to simply let B, C, D, etc. to manage their own Dirty state, and then modified A's IsDirty property as such:
public bool IsDirty
{
get
{
return _isDirty || B.IsDirty || C.IsDirty /* etc */;
}
}
To me, this is not only simple, but it makes the most sense. A is dirty if any of it's properties have changed, and B, C, D, etc are all properties of A.
I didn't test it, but following one should work. I don't remember why, but I think you cannot handle PropertyChanged events. You should declare your own delegate (VoidHandler).
public delegate void VoidHandler(object sender);
public class B // also C,D,E,...
{
// A.ItemChanged() will be wired to this SomethingChangedHandler.
// I heard you are saving. Exclude SomethingChangedHandler from save.
[field: NonSerialized]
public VoidHandler SomethingChangedHandler;
private c;
public C
{
set
{
// unwire handler from old instance of C
if(c != null)
c.SomethingChangedHandler -= ItemChanged;
// wire handler to new instance of C
value.SomethingChangedHandler += ItemChanged;
c = value;
// setting c is also change which require notification
ItemChanged(this);
}
get{}
}
// notify A about any change in B or in C
void ItemChanged(object sender)
{
if(SomethingChangedHandler != null)
SomethingChangedHandler(this);
}
}
For line of business application that have a common base class I do this as per
Implementing INotifyPropertyChanged - does a better way exist?
with some modifications to check for "bubbling" properties.
Base Class
public bool HasAlteredState { get; protected set; }
public event PropertyChangedEventHandler PropertyChanged;
private void propertyObject_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
this.OnPropertyChanged(e.PropertyName);
}
protected virtual void RegisterSubPropertyForChangeTracking(INotifyPropertyChanged propertyObject)
{
propertyObject.PropertyChanged += new PropertyChangedEventHandler(propertyObject_PropertyChanged);
}
protected virtual void DeregisterSubPropertyForChangeTracking(INotifyPropertyChanged propertyObject)
{
propertyObject.PropertyChanged -= propertyObject_PropertyChanged;
}
protected virtual void OnPropertyChanged(string propertyName)
{
this.HasAlteredState = true;
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
protected virtual void OnPropertyChanged<T>(Expression<Func<T>> selectorExpression)
{
if (selectorExpression == null)
throw new ArgumentNullException("selectorExpression");
MemberExpression body = selectorExpression.Body as MemberExpression;
if (body == null)
throw new ArgumentException("The body must be a member expression");
OnPropertyChanged(body.Member.Name);
}
protected bool SetField<T>(ref T field, T value, Expression<Func<T>> selectorExpression)
{
if (EqualityComparer<T>.Default.Equals(field, value)) return false;
if (field is INotifyPropertyChanged)
{
if (field != null) { this.DeregisterSubPropertyForChangeTracking((INotifyPropertyChanged)field); }
}
if (value is INotifyPropertyChanged)
{
if (value != null) { this.RegisterSubPropertyForChangeTracking((INotifyPropertyChanged)value); }
}
field = value;
OnPropertyChanged(selectorExpression);
return true;
}
Sub classes
private IndividualName _name;
public IndividualName PersonName
{
get { return _name; }
set { SetField(ref _name, value, () => PersonName); }
}
Provides
Simple property change notification
Complex property change notification
Event "bubbling" from INotifyPropertyChanged implementations deeper in the object graph
Compile time checking that your property "name" actually refers to your property. i.e. avoid nasty bugs related to spelling a property name wrong when just using a string.
Performance
There is an associated performance hit to this approach... about 20% slower than just using a string. That said although the metrics and tracing say it's slower I cant actually tell the difference so the hit is worth it re: application maintenance for the kinds of apps I'm involved in developing.
Alternative Implementations
If base class is not an option you could go Extension method route.
For better performance you could have two different SetField methods; the first SetNotifyField would deal with properties which themselves implement INotifyPropertyChanged (as above) and the second SetField would deal with simple properties. i.e. cut out the
if (field is INotifyPropertyChanged)...
I have a class that implements INotifyPropertyChanged.
I create an instance of a class in some viewModel.
Is it possible to remove this functionality from the class and inject it after the instance was created? I heard that ICustomTypeDescriptor would make this happen, but i dont know how to use it.
public class C : ICustomNotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public int _id;
public string _name;
public int Id
{
get { return _id; }
set
{
if (_id == value)
{
return;
}
_id = value;
OnPropertyChanged("Id");
}
}
public string Name
{
get { return _name; }
set
{
if (_name == value)
{
return;
}
_name = value;
OnPropertyChanged("Name");
}
}
public void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
If you are just trying to prevent the notifications from being fired when the object is first created and properties set, you can add boolean flag(s) that is/are false until the properties have been set once. You only execute the notification if the flag is true.
Edit:
I don't think there's a clean way to get the functionality in there after removing all the INotifyPropertyChanged code, but there are many ways to control the functionality from outside the instance.
Please note that I wrote all this code in the text editor, not in VisualStudio; it has not been tested in any way.
Add a method to enable notifications:
public class OptionalNotification : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
void OnPropertyChanged(string name) ...
bool _shouldNotify;
public void EnableNotifications()
{
_shouldNotify = true;
}
string _someProperty;
public string SomeProperty
{
get { return _someProperty; }
set
{
if(_someProperty == value) return
_someProperty = value;
if(_shouldNotify) OnPropertyChanged("SomeProperty");
}
}
}
You could do the same thing without the method, if you knew at the time of instantiation whether or not the instance should produce notifications, in which case you'd just need a boolean parameter in the constructor.
Another variation would be to use the Factory pattern, where your Factory has internal access to the boolean flag and sets it upon construction.
Encapsulate the condition in a proxy:
public interface IEntity : INotifyPropertyChanged
{
string SomeProperty { get; set; }
}
public class Entity : IEntity
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string name) ...
string _someProperty;
public string SomeProperty
{
get { return _someProperty; }
set
{
if(_someProperty == value) return
_someProperty = value;
OnPropertyChanged("SomeProperty");
}
}
}
public class EntityNotificationProxy : IEntity
{
IEntity _inner;
public EntityNotificationProxy(IEntity entity)
{
_inner = entity;
_inner.PropertyChanged += (o,e) => { if(ShouldNotify) OnPropertyChanged(o,e); }
}
public bool ShouldNotify { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
void OnPropertyChanged(object sender, PropertChangedEventArgs e)
{
PropertyChangedEventHandler handler = PropertyChanged;
if(handler != null) handler(sender, e);
}
public string SomeProperty
{
get { return _inner.SomeProperty; }
set
{
if(_inner.SomeProperty == value) return
_inner.SomeProperty = value;
}
}
}
Here your consuming classes get the entity proxy instead of the entity itself (but is none the wiser because it references only IEntity when you program to interfaces/abstractions). The wrapping of the proxy can happen in a factory or through an IoC container/DI framework.
The main advantage to this approach is that your entity maintains a pure INotifyPropertyChanged implementation, and the conditional aspect is handled from without. Another advantage is that it helps to enforce programming to abstractions and inversion of control.
The main disadvantage is that you'll need to create proxies for each INotifyPropertyChanged implementation that you want to have this conditional behaviour.
Create a registry to keep track of what instances should or should not raise notifications:
public static class PropertyNotificationRegistry
{
static IDictionary<INotifyPropertyChanged, bool> _registeredClasses
= new Dictionary<INotifyPropertyChanged, bool>;
static void Register(INotifyPropertyChanged o, bool shouldNotify)
{
if(!(_registeredClasses.ContainsKey(o)) _registeredClasses.Add(o, shouldNotify);
// could also implement logic to update an existing class in the dictionary
}
public static void ShouldNotifyWhenPropertiesChange(this INotifyPropertyChanged o)
{
Register(o, true);
}
public static void ShouldNotNotifyWhenPropertiesChange(this INotifyPropertyChanged o)
{
Register(o, false);
}
public static void NotifyPropertyChanged(this INotifyPropertyChanged o, Action notificationAction)
{
if(_registeredClasses.ContainsKey(o))
{
bool shouldNotify = _registeredClasses.Where(x => x.Key == o).Single().Value;
if(shouldNotify) notificationAction();
}
}
}
public class EntityUsingNotificationRegistry : INotifyPropertyChanged
{
... // all the standard INotifyPropertyChanged stuff
string _someProperty;
public string SomeProperty
{
get { return _someProperty; }
set
{
if(_someProperty == value) return;
_someProperty = value;
this.NotifyPropertyChanged(() => OnPropertyChanged("SomeProperty"));
}
}
}
public class SomethingInstantiatingOurEntity
{
public void DoSomething()
{
var entity1 = new EntityUsingNotificationRegistry();
entity1.ShouldNotifyWhenPropertiesChange();
var entity2 = new EntityUsingNotificationRegistry();
entity2.ShouldNotNotifyWhenPropertiesChange();
entity1.SomeProperty = "arbitrary string"; // raises event
entity2.SomeProperty = "arbitrary string"; // does not raise event
var entity3 = new EntityUsingNotificationRegistry();
entity3.SomeProperty = "arbitrary string"; // does not raise event
entity3.ShouldNotifyWhenPropertiesChange();
entity3.SomeProperty = "another arbitrary string"; // now raises event
}
}
Now, the registry has a distinct shortcoming in that it holds references to every instance and will prevent those instances from being picked up by the garbage collector. There may be a solution to this by implementing the registry with WeakReferences, but I'm not up-to-snuff on their usage to recommend a particular implementation.
This will not work. You COULD subclass and inject it, but you would have to change the byte-code to make sure the proper methods are CALLED - and that is the harder method.