© Dmitri Nesteruk 2020
D. NesterukDesign Patterns in .NET Core 3https://doi.org/10.1007/978-1-4842-6180-4_21

21. Observer

Dmitri Nesteruk1  
(1)
St. Petersburg, c.St-Petersburg, Russia
 

Observer

The Observer pattern, quite simply, lets one component notify other components that something happened. The pattern is used all over the place: for example, when binding data to UI, we can program domain objects such that when they change, they generate notifications that the UI can subscribe to and update the visuals.

The Observer pattern is a popular and necessary pattern, so it is not surprising that the designers of C# decided to incorporate the pattern into the language wholesale with the use of the event keyword. The use of events in C# typically uses a convention that mandates the following:
  • Events can be members of a class and are decorated with the event keyword.

  • Event handlers – methods that are called whenever an event is raised – are attached to the event with the += operator and are detached with the -= operator.

  • An event handler typically takes two arguments:
    • An object reference to who exactly fired the event

    • An object that (typically) derives from EventArgs that contains any necessary information about the event

The exact type of an event that is used is typically a delegate. Just like the Action/Func wrappers for lambdas, the delegate wrappers for events are called EventHandler and exist in both nongeneric (that takes an EventArgs) and a generic (that takes a type parameter that derives from EventArgs) second argument. The first argument is always an object.

Here’s a trivial example: suppose, whenever a person falls ill, we call a doctor. First of all, we define event arguments; in our case we just need the address to send the doctor to:
public class FallsIllEventArgs : EventArgs
{
  public string Address;
}
Now, we can implement a Person type, which can look like this:
public class Person
{
  public void CatchACold()
  {
    FallsIll?.Invoke(this,
      new FallsIllEventArgs { Address = "123 London Road" });
  }
  public event EventHandler<FallsIllEventArgs> FallsIll;
}

As you can see, we are using a strongly typed EventHandler delegate to expose a public event. The CatchACold() method is used to raise the event, with the safe access ?. operator being used to ensure that if the event doesn’t have any subscribers, we don’t get a NullReferenceException.

All that remains is to set up a scenario and provide an event handler:
static void Main()
{
  var person = new Person();
  person.FallsIll += CallDoctor;
  person.CatchACold();
}
private static void CallDoctor(object sender, FallsIllEventArgs eventArgs)
{
  Console.WriteLine($"A doctor has been called to {eventArgs.Address}");
}

The event handler can be an ordinary (member) method, a local function, or a lambda – your choice. The signature is mandated by the original delegate; since we’re using a strongly typed EventHandler variant, the second argument is FallsIllEventArgs. As soon as CatchACold() is called, the CallDoctor() method is triggered.

Any given event can have more than one handler (C# delegates are multicast, after all). Removal of event handlers is typically done with the -= operator. When all subscribers have unsubscribed from an event, the event instance is set to null.

Weak Event Pattern

Did you know that .NET programs can have memory leaks? Not in the C++ sense, of course, but it is possible to keep holding on to an object for longer than necessary. Specifically, you can make an object and set its reference to null but it will still be alive. How? Let me show you.

First, let’s make a Button class:
public class Button
{
  public event EventHandler Clicked;
  public void Fire()
  {
    Clicked?.Invoke(this, EventArgs.Empty);
  }
}
So now, let’s suppose we have this button in a window. For the sake of simplicity, I’ll just stick it into a Window constructor:
public class Window
{
  public Window(Button button)
  {
    button.Clicked += ButtonOnClicked;
  }
  private void ButtonOnClicked(object sender, EventArgs eventArgs)
  {
    WriteLine("Button clicked (Window handler)");
  }
  ~Window()
  {
    WriteLine("Window finalized");
  }
}
Looks innocent enough, except it’s not. If you make a button and a window, then set the window to null; it will still be alive! Proof:
var btn = new Button();
var window = new Window(btn);
var windowRef = new WeakReference(window);
btn.Fire();
window = null;
FireGC();
WriteLine($"Is window alive after GC? {windowRef.IsAlive}"); // True

The reason why the window reference is still alive is that it has a subscription to the button. When a button is clicked, the expectation is that something sensible happens: since there is a subscription to this event, the object that happens to have made this subscription cannot be allowed to die, even if the only reference to that object has been set to null. This is a memory leak in the .NET sense.

How can we fix this? One approach would be to use the WeakEventManager class from System.Windows. This class is specifically designed to allow the listener’s handlers to be garbage-collected even if the source object persists. This class is very simple to use:
public class Window2
{
  public Window2(Button button)
  {
    WeakEventManager<Button, EventArgs>
      .AddHandler(button, "Clicked", ButtonOnClicked);
  }
  // rest of class same as before
}

Repeating the scenario again, this Window2 implementation gives a windowRef.IsAlive result of False, as desired.

Event Streams

With all these discussions of Observer, you might be interested to learn that the .NET Framework comes with two interfaces: IObserver<T> and IObservable<T>. These interfaces, which were coincidental with the release of Reactive Extensions (Rx), are meant primarily to deal with reactive streams. While it is not my intention to discuss the entirety of Reactive Extensions, these two interfaces are worth mentioning.

Let’s start with IObservable<T>. This is an interface that is generally similar to the interface of a typical .NET event. The only difference is that instead of using the += operator for subscription, this interface requires that you implement a method called Subscribe(). This method takes an IObserver<T> as its only parameter. Remember, this is an interface, and unlike in the case of events/delegates, there is no prescribed storage mechanism. You are free to use anything you want.

There is some extra icing on the cake: the notion of unsubscription is explicitly supported in the interface. The Subscribe() method returns an IDisposable with the understanding that the return token (Memento pattern at work!) has a Dispose() method that unsubscribes the observer from the observable.

The second piece of the puzzle is the IObserver<T> interface. It is designed to provide push-based notifications through three specific methods:
  • OnNext(T) gets invoked whenever a new event occurs.

  • OnCompleted() gets invoked when the source has no more data to give.

  • OnError() gets invoked whenever the observer has experienced an error condition.

Once again, this is just an interface, and how you handle this is up to you. For example, you can completely ignore both OnCompleted() and OnError().

So, given these two interfaces, the implementation of our trivial doctor-patient example is suddenly a lot less trivial. First of all, we need to encapsulate the idea of an event subscription. The reason why this is required is because we need a memento that implements IDisposable through which unsubscription can happen.
private class Subscription : IDisposable
{
  private Person person;
  public IObserver<Event> Observer;
  public Subscription(Person person, IObserver<Event> observer)
  {
    this.person = person;
    Observer = observer;
  }
  public void Dispose()
  {
    person.subscriptions.Remove(this);
  }
}
This class is an inner class of Person, which is a good hint at the growing complexity of any object that wants to support event streams. Now, coming back to Person, we want it to implement the IObservable<T> interface. But what is T? Unlike the conventional events, there are no guidelines mandating that we inherit from EventArgs – sure, we could continue using that type,1 or we could construct our own, completely arbitrary, hierarchy:
public class Event
{
  // anything could be here
}
public class FallsIllEvent : Event
{
  public string Address;
}
Moving on, we now have a base class Event, so we can declare Person to be a generator of such events. As a consequence, our Person type would implement IObservable<Event> and would take an IObserver<Event> in its Subscribe() method . Here is the entire Person class with the body of the Subscription inner class omitted:
public class Person : IObservable<Event>
{
  private readonly HashSet<Subscription> subscriptions
    = new HashSet<Subscription>();
  public IDisposable Subscribe(IObserver<Event> observer)
  {
    var subscription = new Subscription(this, observer);
    subscriptions.Add(subscription);
    return subscription;
  }
  public void CatchACold()
  {
    foreach (var sub in subscriptions)
     sub.Observer.OnNext(new FallsIllEvent {Address = "123 London Road"});
  }
  private class Subscription : IDisposable { ... }
}

I’m sure you’ll agree that this is a lot more complicated than just publishing a single event for clients to subscribe to! But there are advantages to this: for example, you can choose your own policy with respect to repeat subscriptions, that is, situations when a subscriber is trying to subscribe to some event again. One thing worth noting is that HashSet<Subscription> is not a thread-safe container. This means that if you want Subscribe() and CatchACold() to be callable concurrently, you would need to either use a thread-safe collection, locking or perhaps something even fancier, like an ImmutableList.

The problems don’t end there. Remember, a subscriber has to implement an IObserver<Event> now. This means that to support the scenario we’ve had previously shown, we would have to write the following:
public class Demo : IObserver<Event>
{
  static void Main(string[] args)
  {
    new Demo();
  }
  public Demo()
  {
    var person = new Person();
    var sub = person.Subscribe(this);
  }
  public void OnNext(Event value)
  {
    if (value is FallsIllEvent args)
    WriteLine($"A doctor has been called to {args.Address}");
  }
  public void OnError(Exception error){}
  public void OnCompleted(){}
}

This is, once again, quite a mouthful. We could have simplified the subscription by using a special Observable.Subscribe() static method, but Observable (without the I) is part of Reactive Extensions, a separate library that you may or may not want to use.

So this is how you can build an Observer pattern using .NET’s own interfaces, without using the event keyword. The main advantage of this approach is that the stream of events that is generated by an IObservable can be directly fed into various Rx operators. For example, using System.Reactive, the entire demo program shown earlier can turn into a single statement:
person
  .OfType<FallsIllEvent>()
  .Subscribe(args =>
    WriteLine($"A doctor has been called to {args.Address}"));

Property Observers

One of the most common Observer implementations in .NET is getting notifications when a property changes. This is necessary, for example, to update UI when the underlying data changes. This mechanism uses ordinary events as well as some interfaces that have become standard within .NET.

Property observers can get really complicated, so we’ll cover them in steps, starting from basic interfaces and operations and moving onto the more complicated scenarios.

Basic Change Notification

The central piece of change notification in .NET is an interface called INotifyPropertyChanged:
public interface INotifyPropertyChanged
{
  /// <summary>Occurs when a property value changes.</summary>
  event PropertyChangedEventHandler PropertyChanged;
}
All this event does is expose an event that you’re expected to use. Given a class Person having a property called Age, the typical implementation of this interface looks as follows:
public class Person : INotifyPropertyChanged
{
  private int age;
  public int Age
  {
    get => age;
    set
    {
      if (value == age) return;
      age = value;
      OnPropertyChanged();
    }
  }
  public event PropertyChangedEventHandler PropertyChanged;
  [NotifyPropertyChangedInvocator]
  protected virtual void OnPropertyChanged(
    [CallerMemberName] string propertyName = null)
  {
    PropertyChanged?.Invoke(this,
      new PropertyChangedEventArgs(propertyName));
  }
}

There is a lot to discuss here. First of all, the property gets a backing field. This is required in order to look at the previous value of the property before it is assigned. Notice that the invocation of the OnPropertyChanged() method happens only if the property did change. If it didn’t, there’s no notification.

As far as the IDE-generated OnPropertyChanged() method is concerned, this method is designed to take in the name of the affected property via [CallerMemberName] metadata and then, provided the PropertyChanged event has subscribers, notify those subscribers that the property with that name did, in fact, change.

You can, of course, build your own change notification mechanisms, but both WinForms and WPF are intrinsically aware of INotifyPropertyChanged, as are many other frameworks. So, if you need change notifications, I’d stick to this interface.

A special note needs to be added about INotifyPropertyChanging – an interface that is intended to send events indicating that a property is in the process of changing. This interface is very rarely used, if ever. It would have been nice to be able to use this property to cancel a property change, but sadly the interface makes no provisions for this. In actual fact, cancellation of property changes can be one of the reasons why you would want to implement your own interfaces instead of these.

Bidirectional Bindings

The INotifyPropertyChanged is very useful for notifying the user interface about the change of a property some label is bound to. But what if you have an edit box instead, and that edit box also needs to update the code element behind the scenes?

This is actually doable and doesn’t even result in infinite recursion! This problem generalizes to the following: how do you bind two properties such that changing the one changes the other, in other words, their values are always identical?

Let’s try this. Suppose we have a Product that has a Name and we also have a Window that has a ProductName. We want Name and ProductName to be bound together.
var product = new Product{Name="Book"};
var window = new Window{ProductName = "Book"};
product.PropertyChanged += (sender, eventArgs) =>
{
  if (eventArgs.PropertyName == "Name")
  {
    Console.WriteLine("Name changed in Product");
    window.ProductName = product.Name;
  }
};
window.PropertyChanged += (sender, eventArgs) =>
{
  if (eventArgs.PropertyName == "ProductName")
  {
    Console.WriteLine("Name changed in Window");
    product.Name = window.ProductName;
  }
};

Common sense dictates that this code, when triggered, would cause a StackOverflowException: window affects product, product affects window, and so on. Except it doesn’t happen. Why? Because the setter in both properties has a guard that checks that the value did, in fact, change. If it didn’t, it does a return and no further notifications take place. So we’re safe here.

The preceding solution works, but frameworks such as WinForms try to shrink-wrap situations such as these into separate data binding objects. In a data binding, you specify the objects and their properties and how they tie together. Windows Forms, for example, uses property names (as strings), but nowadays we can be a little bit smarter and use expression trees instead.

So let’s construct a BidirectionalBinding class that will, in its constructor, bind together two properties. For this, we need four pieces of information:
  • The owner of the first property

  • An expression tree accessing the first object’s property

  • The owner of the second property

  • An expression tree accessing the second object’s property

Sadly, it is impossible to reduce the number of parameters in this scenario, but at least they will be more or less human-readable. We’ll also avoid using generics here, though they can, in theory, introduce additional type safety.

So, here is the entire class:
public sealed class BidirectionalBinding : IDisposable
{
  private bool disposed;
  public BidirectionalBinding(
INotifyPropertyChanged first, Expression<Func<object>> firstProperty,
    INotifyPropertyChanged second, Expression<Func<object>> secondProperty)
  {
    if (firstProperty.Body is MemberExpression firstExpr
        && secondProperty.Body is MemberExpression secondExpr)
    {
      if (firstExpr.Member is PropertyInfo firstProp
          && secondExpr.Member is PropertyInfo secondProp)
      {
        first.PropertyChanged += (sender, args) =>
        {
          if (!disposed)
          {
            secondProp.SetValue(second, firstProp.GetValue(first));
          }
        };
        second.PropertyChanged += (sender, args) =>
        {
          if (!disposed)
          {
            firstProp.SetValue(first, secondProp.GetValue(second));
          }
        };
      }
    }
  }
  public void Dispose()
  {
    disposed = true;
  }
}
The preceding code depends on a number of preconditions regarding the expression trees, specifically these:
  • Each expression tree is expected to be a MemberExpression.

  • Each member expression is expected to access a property (thus, PropertyInfo).

If these conditions are met, we subscribe each property to each other’s changes. There’s an additional dispose guard added to this class to allow the user to stop processing the subscriptions if necessary.

The preceding is a trivial example of the kinds of things that can happen behind the scenes in frameworks that intend to intrinsically support data binding.

Property Dependencies

In Microsoft Excel, you can have cells contain calculations using values of other cells. This is very convenient: whenever a particular cell’s value changes, Excel recalculates every single cell (including cells on other sheets) that this cell affected. And then those cells cause the recalculation of every cell dependent on them. And so it goes forever until the entire dependency graph is traversed, however long it takes. It’s beautiful.

The problem with properties (and with the Observer pattern generally) is exactly the same: sometimes a part of a class not only generates notifications but affects other parts of the class, and then those members also generate their own event notifications. Unlike Excel, .NET doesn’t have a built-in way of handling this, so such a situation can quickly turn into a real mess.

Let me illustrate. People aged 16 or older (could be different in your country) can vote, so suppose we want to be notified of changes to a person’s voting rights:
public class Person : PropertyNotificationSupport
{
  private int age;
  public int Age
  {
    get => age;
    set
    {
      if (value == age) return;
      age = value;
      OnPropertyChanged();
    }
  }
  public bool CanVote => Age <= 16;
}

In the preceding code, changes to a person’s age should affect their ability to vote. However, we would also expect to generate appropriate change notifications for CanVote… but where? After all, CanVote has no setter!

You could try to put them into the Age setter, for example:
public int Age
{
  get => age;
  set
  {
    if (value == age) return;
    age = value;
    OnPropertyChanged();
    OnPropertyChanged(nameof(CanVote));
  }
}
This will work, but consider a scenario: what if age changes from 5 to 6? Sure, the age has changed, but CanVote has not, so why are we unconditionally doing a notification on it? This is incorrect. A functionally correct implementation would have to look something like the following:
set
{
  if (value == age) return;
  var oldCanVote = CanVote;
  age = value;
  OnPropertyChanged();
  if (oldCanVote != CanVote)
    OnPropertyChanged(nameof(CanVote));
}

As you can see, the only way to determine that CanVote has been affected is to cache its old value, perform the changes on age, then get its new value and check if it’s been modified, and only then perform the notification.

Even without this particular pain point, the approach we’ve taken with property dependencies does not scale. In a complicated scenario where properties depend on other properties, how are we expected to track all the dependencies and make all the notifications? Clearly, some sort of centralized mechanism is needed to track all of this automatically.

Let’s build such a mechanism. We’ll construct a base class called PropertyNotificationSupport that will implement INotifyPropertyChanged and will also take care of dependencies. Here is its implementation:
public class PropertyNotificationSupport : INotifyPropertyChanged
{
  private readonly Dictionary<string, HashSet<string>> affectedBy
    = new Dictionary<string, HashSet<string>>();
  public event PropertyChangedEventHandler PropertyChanged;
  [NotifyPropertyChangedInvocator]
  protected virtual void OnPropertyChanged
    ([CallerMemberName] string propertyName = null)
  {
    PropertyChanged?.Invoke(this,
      new PropertyChangedEventArgs(propertyName));
    foreach (var affected in affectedBy.Keys)
      if (affectedBy[affected].Contains(propertyName))
        OnPropertyChanged(affected);
  }
  protected Func<T> property<T>(string name,
    Expression<Func<T>> expr) { ... }
  private class MemberAccessVisitor : ExpressionVisitor { ... }
}

This class is complicated, so let’s go through this slowly and figure out what’s going on here.

First, we have affectedBy, which is a dictionary that lists every property and a HashSet of properties affected by it. For example, if voting ability is affected by age and whether or not you’re a citizen, this dictionary will contain a key of "CanVote" and values of {"Age", "Citizen"}.

We then modify the default OnPropertyChanged() implementation to ensure that the notifications happen both on the property itself and all properties it affects. The only question now is – how do properties get enlisted in this dictionary?

It would be too much to ask the developers to populate this dictionary by hand. Instead, we do it automatically with the use of expression trees. A getter for a read-only property is provided to the base class as an expression tree, which completely changes the way dependent properties are constructed:
public class Person : PropertyNotificationSupport
{
  private readonly Func<bool> canVote;
  public bool CanVote => canVote();
  public Person()
  {
    canVote = property(nameof(CanVote),
      () => Citizen && Age >= 16);
  }
  // other members here
}
Clearly, everything has changed. The property is now initialized inside the constructor using the base class’ property() method. That property takes an expression tree, parses it to find the dependency properties, and then compiles the expression into an ordinary Func<T>:
protected Func<T> property<T>(string name, Expression<Func<T>> expr)
{
  Console.WriteLine($"Creating computed property for expression {expr}");
  var visitor = new MemberAccessVisitor(GetType());
  visitor.Visit(expr);
  if (visitor.PropertyNames.Any())
  {
    if (!affectedBy.ContainsKey(name))
      affectedBy.Add(name, new HashSet<string>());
    foreach (var propName in visitor.PropertyNames)
      if (propName != name) affectedBy[name].Add(propName);
  }
  return expr.Compile();
}
The parsing of the expression tree is done using a MemberAccessVisitor, a private, nested class that we’ve created. This class goes through the expression tree looking for member access and collects all the property names into a simple list:
private class MemberAccessVisitor : ExpressionVisitor
{
  private readonly Type declaringType;
  public readonly IList<string> PropertyNames = new List<string>();
  public MemberAccessVisitor(Type declaringType)
  {
    this.declaringType = declaringType;
  }
  public override Expression Visit(Expression expr)
  {
    if (expr != null && expr.NodeType ==  ExpressionType.MemberAccess)
    {
      var memberExpr = (MemberExpression)expr;
      if (memberExpr.Member.DeclaringType == declaringType)
      {
        PropertyNames.Add(memberExpr.Member.Name);
      }
    }
    return base.Visit(expr);
  }
}

Notice that we restrict ourselves to the declaring type of the owning class – handling a situation with property dependencies between classes is doable, but a lot more complicated.

Anyways, putting all of this together, we can now write something like the following:
var p = new Person();
p.PropertyChanged += (sender, eventArgs) =>
{
  Console.WriteLine($"{eventArgs.PropertyName} has changed");
};
p.Age = 16;
// Age has changed
// CanVote has changed
p.Citizen = true;
// Citizen has changed
// CanVote has changed

So it works. But our implementation is still far from ideal. If I were to change the age to 10 in the preceding code, CanVote would still receive a notification, even though it shouldn’t! That’s because, at the moment, we’re firing these notifications unconditionally. If we wanted to fire these only when the dependent properties have changed, we would have to resort to INotifyPropertyChanging (or a similar interface) where we would have to cache the old value of every affected property until the INotifyPropertyChanged call and then check that those have, in fact, changed. I leave this as an exercise for the reader.

Finally, a small note. You can see some overcrowding happening inside property setters. Three lines is already a lot, but if you factor in additional calls, such as the use of INotifyPropertyChanging, then it makes sense to externalize the entire property setter. Turning each property into a Property<T> (see the “Property Proxy” section of the Proxy pattern) is a bit overkill, but we can imbue the base class with something like
protected void setValue<T>(T value, ref T field,
  [CallerMemberName] string propertyName = null)
{
  if (value.Equals(field)) return;
  OnPropertyChanging(propertyName);
  field = value;
  OnPropertyChanged(propertyName);
}
with properties now simplifying to
public int Age
{
  get => age;
  set => setValue(value, ref age);
}

Notice that, in the preceding code, we must do propertyName propagation because the [CallerMemberName] attribute inside OnPropertyChanged() will no longer work for us out of the box.

Views

There’s a big, huge, glaring problem with property observers: the approach is intrusive and clearly goes against the idea of Separation of Concerns. Change notification is a separate concern, so adding it right into your domain objects might not be the best idea.

Why not? Well, imagine you decide to change your mind and move from the use of INotifyPropertyChanged (INPC) to the user of the IObservable interface. If you were to scatter INPC use throughout your domain objects, you’d have to meticulously go through each one, modifying each property to use the new paradigm, not to mention the fact that you’d have to modify those classes as well to stop using the old interfaces and start using the new ones. This is tedious and error-prone, and precisely the kind of thing we’re trying to avoid.

So, if you want change notifications handled outside of the objects that change, where would you add them? It shouldn’t be hard – after all, we’ve seen patterns such as Decorator that are designed for this exact purpose.

One approach is to put another object in front of your domain object that would handle change notifications and other things besides. This is what we would typically called a view – it is this thing that would be bound to UI, for example.

To use views, you would keep your objects simple, using ordinary properties (or even public fields!) without embellishing them with any extra behaviors:
public class Person
{
  public string Name;
}
In fact, it’s worth keeping the data objects as simple as possible; this is what’s known as a data class in languages such as Kotlin. Now what you do is build a view on top of the object. The view can incorporate other concerns, including property observers:
public class PersonView : View
{
  protected Person person;
  public PersonView(Person person)
  {
    this.person = person;
  }
  public string Name
  {
    get => person.Name;
    set {
      setValue(value, () => person.Name);
    }
  }
}

The preceding code is, of course, a Decorator. It wraps the underlying object with mirroring getters/setters that perform the notifications. If you need even more complexity, this is the place to get it. For example, if you want property dependencies tracked in an expression tree, you’d do it in this constructor rather than in the constructor of the underlying object.

You’ll notice that, in the preceding listing, I’m trying to be sly by hiding any implementation details. We simply inherit from some class View and we don’t really care how it handles notifications: maybe it uses INotifyPropertyChanged, maybe it uses IObservable, maybe something else. We don’t care.

The only real issue is how to call this class’ setter considering that we want it to have information about both the name of the property we’re assigning (just in case it’s needed) and the value being assigned. There’s no uniform solution to this problem, and obviously, the more information you pack into this improvised setValue() method, the better. If person.Name had been a field, things would be greatly simplified because we could simply pass a reference to that field to be assigned, but we’d still have to pass a nameof() for the base class to notify via INPC if necessary.

Observable Collections

If you bind a List<T> to a list box in either WinForms or WPF, changing the list won’t update the UI. Why not? Because List<T> does not support the Observer pattern, of course – its individual members might, but the list as a whole has no explicit way of notifying that its contents have changed. Admittedly, you could just make a wrapper where methods such as Add() and Remove() generate notifications. However, both WinForms and WPF come with observable collections – classes BindingList<T> and ObservableCollection<T>, respectively.

Both of these types behave as a Collection<T>, but the operations generate additional notifications that can be used, for example, by UI components to update the presentation layer when the collections change. For example, ObservableCollection<T> implements the INotifyCollectionChanged interface which, in turn, has a CollectionChanged event. This event will tell you what action has been applied to the collection and will give you a list of both old and new items, as well as information about old and new starting indices: in other words, you get everything that you need in order to, say, redraw a list box correctly depending on the operation.

One important thing to note is that neither BindingList<T> nor ObservableCollection<T> is thread-safe. So if you plan to read/write these collections from multiple threads, you’d need to build a threading proxy (hey, Proxy pattern!). There are, in fact, two options here:
  • Inherit from an observable collection and just put common collection operations such as Add() behind a lock.

  • Inherit from a concurrent collection (e.g., a ConcurrentBag<T>) and add INotifyCollectionChanged functionality.

You can find implementations of both of these approaches on StackOverflow and elsewhere. I prefer the first option as it’s a lot simpler.

Observable LINQ

When we discussed property observers, we also managed to discuss the idea of properties that affect other properties. But that’s not all they affect. For example, a property might be involved in a LINQ query which yields some result. So, how can we know that we need to requery the data in a particular query when a property it depends on changes?

Over time, there had been frameworks such as CLINQ (Continuous LINQ ) and Bindable LINQ that attempted to solve the problem of a LINQ query generating the necessary events (i.e., CollectionChanged) when one of its constituent parts failed. Other frameworks exist, and I can offer no recommendations here about which one I would recommend you use. Keep in mind that these frameworks are attempting to solve a really difficult problem.

Declarative Subscriptions in Autofac

So far, most of our discussions have centered around the idea of explicit, imperative subscription to events, whether via the usual .NET mechanisms, Reactive Extensions, or something else. However, that’s not the only way event subscriptions can happen.

You can also define event subscriptions declaratively. This is often made possible by the fact that applications make use of a central IoC container where the declarations can be found and event wireups can be made behind the scenes.

There are two popular approaches for declarative event wireups. The first uses attributes: you simply mark some method as [Publishes("foo")] and some other method, in some other class, as [Subscribes("foo")] and the IoC container makes a connection behind the scenes.

An alternative is to use interfaces, and that is what we are going to demonstrate, together with the use of the Autofac library. First, we define the notion of an event and flesh out interfaces for the sending of the event and its handling:
public interface IEvent {}
public interface ISend<TEvent> where TEvent : IEvent
{
  event EventHandler<TEvent> Sender;
}
public interface IHandle<TEvent> where TEvent : IEvent
{
  void Handle(object sender, TEvent args);
}
We can now manufacture concrete implementations of events. For example, suppose we are handling click events, where a user can press a button a certain number of times (e.g., double-click it):
public class ButtonPressedEvent : IEvent
{
  public int NumberOfClicks;
}
We can now make a Button class that generates such events. For simplicity, we’ll simply add a Fire() method that fires off the event. Adopting a declarative approach, we decorate the Button with the ISend<ButtonPressedEvent> interface:
public class Button : ISend<ButtonPressedEvent>
{
  public event EventHandler<ButtonPressedEvent> Sender;
  public void Fire(int clicks)
  {
    Sender?.Invoke(this, new ButtonPressedEvent
    {
      NumberOfClicks = clicks
    });
  }
}
And now, for the receiving side, suppose we want to log button presses. This means we want to handle ButtonPressedEvents, and luckily, we already have an interface for this, too:
public class Logging : IHandle<ButtonPressedEvent>
{
  public void Handle(object sender, ButtonPressedEvent args)
  {
    Console.WriteLine(
      $"Button clicked {args.NumberOfClicks} times");
  }
}
Now, what we want, behind the scenes, is for our IoC container to automatically subscribe Logging to the Button.Sender event behind the scenes, without us having to do this manually. Let me first of all show you the monstrous piece of code that you would require to do this:
var cb = new ContainerBuilder();
var ass = Assembly.GetExecutingAssembly();
// register publish interfaces
cb.RegisterAssemblyTypes(ass)
  .AsClosedTypesOf(typeof(ISend<>))
  .SingleInstance();
// register subscribers
cb.RegisterAssemblyTypes(ass)
  .Where(t =>
    t.GetInterfaces()
      .Any(i =>
        i.IsGenericType &&
        i.GetGenericTypeDefinition() == typeof(IHandle<>)))
  .OnActivated(act =>
  {
    var instanceType = act.Instance.GetType();
    var interfaces = instanceType.GetInterfaces();
    foreach (var i in interfaces)
    {
      if (i.IsGenericType
          && i.GetGenericTypeDefinition() == typeof(IHandle<>))
      {
        var arg0 = i.GetGenericArguments()[0];
        var senderType = typeof(ISend<>).MakeGenericType(arg0);
        var allSenderTypes =
          typeof(IEnumerable<>).MakeGenericType(senderType);
        var allServices = act.Context.Resolve(allSenderTypes);
        foreach (var service in (IEnumerable) allServices)
        {
          var eventInfo = service.GetType().GetEvent("Sender");
          var handleMethod = instanceType.GetMethod("Handle");
          var handler = Delegate.CreateDelegate(
            eventInfo.EventHandlerType, null, handleMethod);
          eventInfo.AddEventHandler(service, handler);
        }
      }
    }
})
.SingleInstance()
.AsSelf();
Let’s go through what’s happening in the preceding code step by step.
  • First, we register all assembly types that implement ISend<>. There are no special steps that need to be taken there because they just need to exist somewhere. For the sake of simplicity, we register them as singletons – if that were not the case, the situation with the wireups would become even more complicated because the system would have to track each constructed instance.2

  • We then register the types that implement IHandle<>. This is where things get crazy because we specify an additional OnActivated() step that must be performed before an object is returned.

  • Then, given this IHandle<Foo> type, we locate all types which implement the ISend<Foo> interface using reflection. This is a rather tedious process.

  • For each of the located types, we wire up the subscription. Again, this is done using reflection, and you can also see some magic strings here and there.

With this setup in place, we can build the container and resolve both a Button and a Logging component, and the subscriptions will be done behind the scenes:
var container = cb.Build();
var button = container.Resolve<Button>();
var logging = container.Resolve<Logging>();
button.Fire(1); // Button clicked 1 times
button.Fire(2); // Button clicked 2 times

In a similar fashion, you could implement declarative subscriptions using attributes instead. And if you don’t use Autofac, don’t worry: most popular IoC containers are capable of implementing these sorts of declarative event wireups.

Summary

Generally speaking, we could have avoided discussing the Observer pattern in C# because the pattern itself is baked right into the language. That said, I have demonstrated some of the practical uses of the Observer (property change notifications) as well as some of the issues related to that (dependent properties). Furthermore, we looked at the way in which the Observer pattern is supported for reactive streams.

Thread safety is one concern when it comes to Observer, whether we are talking about individual events or entire collections. The reason why it shows up is because several observers on one component form a list (or similar structure) and then the question immediately arises as to whether that list is thread-safe and what exactly happens when it’s being modified and iterated upon (for purposes of notification) at the same time.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.220.187.178