Click here to Skip to main content
15,886,199 members
Articles / Programming Languages / C#
Article

Event Like Processing for Properties

Rate me:
Please Sign up or sign in to vote.
4.12/5 (7 votes)
18 Jan 2007CPOL7 min read 28K   148   21   4
Attach delegates to properties for invocation when property value changes.

Sample Image - PropertyWatch.png

Introduction

I've long wished it was easier to monitor variables, i.e., to observe changes in their values without having to disturb normal processing flow - sort of like a database trigger (although that's only occurred to me since writing PropertyWatch, and I've never actually used a database trigger).

The introduction of the “Property” into contemporary languages offers an opportunity to fulfill my desires; when I first encountered C# (i.e.., when I was calling it C ‘crunch'), I was disappointed on finding neither the language nor the environment appeared to provide out-of-the-box property events. I can only envisage the need for two such events - ReadProperty and ChangePropertyValue; it is the latter that PropertyWatch implements, I'll attend to the the former if and when needed.

Using the code

The PropertyWatch class provides three methods, each having instance and static variants, a delegate definition, and an EventArgs derivative; there are no public properties or fields. I've taken out the Singleton implementation, argument checks, and exception handling; their inclusion would necessitate shipping other parts of my Toolkit, which would obfuscate the issue addressed by this article.

At the heart of PropertyWatch lies a hierarchical collection object of the form:

C#
Dictionary<object, Dictionary<string, List<Observer>>>

Working left-to-right, object is an instance object whose properties are under observation, string is the name of the property being observed, and Observer is the delegate method that's invoked when the value of the property changes. The collection implementing class, observers, is private and nested within PropertyWatch. The public Observer delegate and its companion EventData class are also nested within PropertyWatch. The three methods provided by PropertyWatch are, Attach, Detach, and Notify, whose definitions are thus:

C#
public bool Attach(string _propertyName, Observer _observer);
public static bool Attach(object _instanceObject, string _propertyName, Observer _observer);
public bool Detach(string _propertyName, Observer _observer);
public static bool Detach(object _instanceObject, string _propertyName, Observer _observer);
public void Notify(object _newValue);
public static void Notify(object _instanceObject, object _newValue);
private static void notify(MethodBase _caller, object _instanceObject, object _newValue);

The Attach and Detach methods are similar in purpose to the add and remove accessors of an event (often accessed via the “+=” and “-=” operators). The arguments are hopefully self explanatory; these methods do what they say, attach and detach an observer to monitor changes to the value of a property of an instance of the property's declaring class.

The Notify method must be invoked by a set accessor, a primary objective was to minimise the arguments required. If the property declaring type inherits from PropertyWatch, then the instance variant of Notify can be invoked, which only requires the value as an argument

C#
public string Property
{
   get{return this.backingVariable;}
   set
   {
      PropertyWatch.Notify(this, value); // static variant
      this.Notify(value);                // instance variant
      this.backingVariable = value;
   }
}

Finally, there's a sealed class Tools that contains a couple of support methods which I discuss in the following Points of interest section; these are in a separate class because the methods have wider applicability than in the PropertyWatch alone.

Points of interest

  1. Why not use normal event handling? That's what I assumed I would do, but I couldn't figure out a way of doing so which didn't involve constructing and emitting events at runtime - which felt like and looked like a sledgehammer. So, I opted for using the hierarchical collection class, as it best represents my view of the problem. If anyone can suggest a simple event based implementation, please do so - I get as much enjoyment from trashing code as I do from scratching it out in the first place. As you can see in the code, there's not much to it.
  2. Why not set the property value in Notify? The SetValue method provided by PropertyInfo invokes the set accessor, hence we end up in a recursive situation. If we detect the recursive situation by interrogating the stack in the set accessor to avoid calling Notify again, that doesn't solve the problem - a statement of the form this.backingVariable = value;, has to be written somewhere - what better place than in the set accessor itself..
  3. I don't think setting the value in Notify can be done, no matter how hard we try - the property itself has no cognizance of backing variables, and nor should it - there may not even be such, the value might be packaged up in a message and set into the ether to who knows where. So, the principle is that the set accessor is not relieved of any if its “normal” duties.

  4. Why must the set accessor pass its class instance to the static implementation of Notify, can't Notify work it out? Well, not as easily as I assumed it would be, the design only had the value being passed. But, I can't see an easy way of getting the caller's “this” out of MethodBase or PropertyInfo without going down the RelectedType track, which I know from experience is hard going. It was this shortcoming that led to the provision of instance implementations of the three methods - in practice, I've found that I use the static method variants as I usually have another class that I want to use as the base for my data classes - if C# had multiple inheritance, then..., but that's another story.
  5. You'll observe that the scheme relies on property names, the values of which have to come from somewhere. The Notify method derives the property name from the set accessor method name, using it to get the PropertyInfo and to access PropertyWatch's Observers collection. However, in the Attach/Detach methods, I can see no convenient, or for that matter inconvenient, way of getting the property name from a reliable source. Hence, I've adopted a clumsy scheme involving a struct of static strings as is implemented in the PropertyWatchData class; originally, this had the property names as constants, but red is to me as it is to a bull. Which is where the support method PropertyWatch.GetPropertyName comes into play.
  6. The default constructor must initialise all the properties; note, this means that if PropertyWatch is used on structs, the constructor must initialise all the fields and all the properties - don't ask me why the former is necessary, I don't know either. Each property set accessor must interrogate the IsConstructing property; if it's true, then the Tools.GetPropertyName method is invoked, and its result is stored in the appropriate static variable in the <dataClass>.PropertyNames struct. I changed a property name from Balance to AccountBalance, and I changed the corresponding backing variable name similarly, but I willfully neglected to change the corresponding name in PropertyNames, but the scheme still works, and you'll see that the ObservationsLog reports the property name correctly as AccountBalance.

    You'll note that the IsConstructing property doesn't have a backing variable, rather it returns the result from the static method Tools.DefaultConstructing, which interrogates the stack to determine if it contains the default constructor; if so, then true is returned; otherwise, false is returned (as well as red code, I hate flag variables - perhaps even more so).

    Again - suggestions for improvement will be gratefully and gracefully received.

  7. My code has no attribution, use it in any way you see fit, including passing it off as your own if you're so inclined, it's thee who must live with thine deceit, not I.
  8. I've used terms such as Observer that I know others use in special contexts such as Patterns and Factories, I make no claim to have implemented such here. I've been using terms like Observer for more years than many of those, who insist that such words be reserved for their specific contextual meaning, have walked this earth.

    I make no apologies for offending such people; programmers don't invent most of the words they use, they purloin them from the English language at large. A major reason English is the most widely spoken language is that there is no language police, unlike other languages such as French, German, Mandarin etc. - let's not allow programmers (even if their name is Bill Gates) become the English Language Police (ELP).

  9. Why do I write long lines of code? Because I have a milspec 23” hi-res CRT that I run 1920x1440 and sometimes higher. It only cost me $AUS65.00 ($US50, ₤UK25, €EU35) - I've not seen an LCD or Plasma to touch its clarity and vibrant, yet soft, colours - sure, it uses a lot of real estate and weighs a massive 43 KG, but it's black, and it's beautiful. Of course, its low price (at a government auction) is entirely due to the advent of flat screens.

History

  • 13/01/07
    • Late inclusions in version 1.0.
    • PropertyWatch implements IsConstructing as a protected property, obviating the necessity of implementing IsConstructing in classes that have PropertyWatch as their base.
    • PropertyWatch defines a corresponding interface for data classes that do not inherit from PropertyWatch.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Australia Australia
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionWhat about INotifyPropertyChanged? Pin
Marek Istvanek22-Jan-07 19:27
Marek Istvanek22-Jan-07 19:27 
AnswerRe: What about INotifyPropertyChanged? Pin
urbane.tiger23-Jan-07 13:20
urbane.tiger23-Jan-07 13:20 
GeneralRe: What about INotifyPropertyChanged? Pin
C# Genius26-Jan-07 18:44
C# Genius26-Jan-07 18:44 
GeneralRe: What about INotifyPropertyChanged? Pin
urbane.tiger28-Jan-07 13:47
urbane.tiger28-Jan-07 13:47 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.