Search code examples
c#reflectionpropertychangelistenernested-properties

c# on nested property change tracking


I have a generic class with a single argument that represents an Element of a third party DLL for the purpose of serialization of objects of T kind. What I would like to do is add a 'Dirty' map to my class and lazily trigger it whenever one of my Element's nested properties are changed.

Is it possible to when the property is accessed catch the request and identify what property is changing? That if a SET is being performed I can log that sub-property P is now dirty and needs to be saved? Or at least a single bit that indicates that SOMETHING has changed?

  public class ResourceSerializer<T>
  where T : Base, new()
  {
   T element;
   Dictionary<String,Boolean> dirtyMap;

   public T Element { get { return this.getElement(); } }
   public Boolean IsDirty { get; private set; }

   public ResourceSerializer()
   {
     dirtyMap = new Dictionary<string,bool>();
     element = new T();
     // code to reflect back upon T's Properties and build out the dirtyMap. 
     // I already can do this I just omitted it.
     // in my Person example there would be keys:  'FirstName', 'LastName', 'Age', 'Gender', 'PrimaryAddress'
    }


   // how can I call this programmatically?
   void flagDirty(String property)
   {
     dirtyMap[property] = true;
     this.IsDirty = true;
   }
   T getElement()
   {
     // In case I need to do a thing before returning the element. 
     // Not relevant to the question at hand.
     return this.element;
   }
 }

a somewhat advanced example of 'Base'. You can see how I need to recurse my actions as not everything is a primitive. I have a manager level class that logs all of these ResourceSerializer objects.

 public class Base
 {
   public Base()
   {

   }
 }
 public enum gender
 {
   Male,
   Female,
   Other,
   Unspecified,
 }
  public class Address : Base
 {
   public String Street { get; set; }
   public String State { get; set; }
   public String Zip { get; set; }
   public Address() : base()
   {

   }
 }
 public class Person : Base
 {
   public String FirstName { get; set; }
   public String LastName { get; set; }
   public Int16 Age { get; set; }
   public gender Gender { get; set; }
   public Address PrimaryAddress { get; set; }
   public Person() : base()
   {

   }
 }
 public class Patient : Person
 {
   public Person PrimaryContact { get; set; }
   public Patient() : base()
   {

   }
 }

and a small class i would turn into a test method later..

  public class DoThing
  {
    public DoThing()
    {
      ResourceSerializer<Person> person = new ResourceSerializer<Person>();
      person.Element.Age = 13; // catch this and mark 'Age' as dirty.
    }
  }

Solution

  • Without a custom setter no, there's nothing to do that.

    The usual pattern for what you're trying to do is implement the INotifyPropertyChanged interface, that interface is precisely created for classes (or structs) which need to track and inform about changes on their properties.

    If you're lazy as me, I would create an analyzer which at the beginning of my app scans all my classes which are tagged with an attribute and with all properties created as virtual, then using codedom I would create a new class which would inherit from the found class and it implements the INotifyPropertyChanged, then you can have a generic Factory which returns instances of these new classes when the type of the generic call is of a known registered type.

    I've used this before for classes which I wanted to have remote properties, just tagged the class and my scan system rewrote the getter/setter to do the remote calls transparently, the concept at the end is the same.

    It's a lot of work at the begining, but if you have a ton of classes it will be a lot less of code to write than implementing INotifyPropertyChanged on all your classes.