Properties with events: another attempt

See also: Properties as objects

The goal here is to have a class with a property that implements the Observer pattern, based on a statement in Object Thinking that all classes should support this. (I’m not sure I agree, but I’m exploring it anyway.)

So far I’ve proposed two options:

Let the class support its own notifications of events. Downsides are:

  • Duplicated code as soon as another property wants the same feature.
  • 11 lines of text in my class to support the basic notion of an object with an attribute
  • Possibility of a bug where the class directly modifies the backing store, without firing the OnSet event.

Create a class to do it all for you. Downsides are:

  • Can’t limit access to the setter to just the containing class.
  • Violates encapsulation rule of Law of Demeter by exposing an implementation detail (that I’m using Property<>).

Upon further discussion with members of the team, we’ve come up with something I like quite a bit. It balances responsibilities between the class you’re writing and the Property<> class. It seems to solve all the problems listed above.

Here’s the code:

      interface IReadonlyProperty<T>

      {

            T Value { get; }

            event Property<T>.SetEventHandler OnSet;

      }

      class Property<T> : IReadonlyProperty<T>

      {

            T _value;

            public Property()

            {

                  // put something into the event so that I don't have to check

                  // for null later.

                  this.OnSet += delegate { };

            }

            public delegate void SetEventHandler(object sender, T newValue);

            public event SetEventHandler OnSet;

            public T Value

            {

                  get

                  {

                        return this._value;

                  }

                  set

                  {

                        this._value = value;

                        this.OnSet(this, this._value);

                  }

            }

            public void SetValueWithoutFiringEvent(T t) { this._value = t; }

            T IReadonlyProperty<T>.Value { get { return this._value; } }

      }

To demonstrate its worth, here’s the usage:

      class Customer

      {

            Property<int> _age = new Property<int>();

            public IReadonlyProperty<int> Age

            {

                  get { return this._age; }

            }

            public void Test()

            {

                  int eventFiredCount = 0;

                  this._age.OnSet += delegate { eventFiredCount++; };

                  Debug.Assert(eventFiredCount == 0);

                  this._age.Value = 9;

                  Debug.Assert(eventFiredCount == 1);

                  this._age.SetValueWithoutFiringEvent(7);

                  Debug.Assert(eventFiredCount == 1);

            }

      }

Note that I can still set the value internally to Customer without firing the event, but only by explicitly saying that’s what I’m doing. I could decide never to allow that by just removing the SetValueWithoutFiringEvent method.