Archive

Archive for November, 2008

A Covariant ObservableCollection for .NET 3.x

November 28th, 2008

When starting with generics, I was somewhat suprised that something like this didn’t work:

 

public interface IAnimal
{
}

public class Pig : IAnimal
{
}

public class AnimalFarm
{
  private ObservableCollection<Pig> pigs;

  public IEnumerable<IAnimal> Animals
  {
    get { return pigs; } //DOES NOT COMPILE
  }
}

 

The problem is that generics aren’t covariant, which is sometimes a bit of a problem when working with interfaces. However, while we’re waiting for C# 4.0, there is a poor man’s solution to covariance – the idea is to just expose the required IEnumerable<IAnimal> interface explicitly for the interface. And of course, there’s a generic solution to that problem:

 

/// <summary>
/// An implementation of <see cref="ObservableCollection{T}"/> that provides
/// an <see cref="IEnumerable{X}"/> interface for a super type of
/// <typeparamref name="T"/>.
/// </summary>
/// <typeparam name="T">The type of items to be stored in the collection.</typeparam>
/// <typeparam name="X">A super type of <typeparamref name="X"/>, for which this
/// collection provides an <see cref="IEnumerable{X}"/> interface, thus providing
/// covariance.</typeparam>
public class HybridObservableCollection<T, X> : ObservableCollection<T>,
                                                IEnumerable<X> where T : X
{

  /// <summary>
  /// Provides enumeration over type <see cref="X"/>.
  /// </summary>
  /// <returns>A <see cref="IEnumerator{X}"/>> that can be used to iterate
  /// through the collection.</returns>
  IEnumerator<X> IEnumerable<X>.GetEnumerator()
  {
    foreach (T t in this)
    {
      yield return t;
    }
  }

}

 

Note the type constraint: The second type parameter (X) must be convertible to the first one (T), which ensures that you can’t break the collection.

And as a result, we can return the collection as both IEnumerable<Pig> or IEnumerable<IAnimal>:

public class AnimalFarm
{
  //the collection provides both IEnumerable<Pig>, IEnumerable<IAnimal>
  private HybridObservableCollection<Pig, IAnimal> pigs;

  public IEnumerable<IAnimal> Animals
  {
    get { return pigs; } //WORKS
  }

  public IEnumerable<Pig> Pigs
  {
    get { return pigs; } //WORKS TOO
  }
}

 

Of course, rather than just IEnumerable<IAnimal>, you could easily expose IList<IAnimal> that way, but you would risk runtime exceptions if somebody tried to inject another IAnimal implementation into the base class that is not an instance of type Pig. However, in a safe environment, this might be well feasible if it lets you expose your collections as lists and still stick to interfaces.

Author: Categories: C#, Uncategorized, WPF Tags:

Generic Extension Methods to Test INotifyPropertyChanged

November 22nd, 2008

Just two extension methods I wrote in order to simplify testing of classes that implement INotifyPropertyChanged.

The first one just checks that the PropertyChanged event fires once and for the specified property:

 
/// <summary>
/// Checks whether a given action produces a change event for
/// a given property.
/// </summary>
/// <typeparam name="T">A class that implements <see cref="INotifyPropertyChanged"/>.
/// </typeparam>
/// <param name="host">The object that contains the changed property.</param>
/// <param name="changeAction">An action that triggers the
/// <see cref="INotifyPropertyChanged.PropertyChanged"/> event.</param>
/// <param name="propertyName">The name of the changed property, as advertised by the
/// <see cref="INotifyPropertyChanged.PropertyChanged"/> event.</param>
public static void AssertFiresPropertyChangedEvent<T> (this T host, string propertyName,
                                                       Action<T> changeAction)
                                                       where T : INotifyPropertyChanged
{
  bool fired = false;
  
  //register handler with assertion
  PropertyChangedEventHandler handler = (sender, e) =>
    {
      fired = true;
      Assert.AreEqual(propertyName, e.PropertyName);
    };

  //register listener, trigger action, then deregister
  host.PropertyChanged += handler;
  changeAction(host);
  host.PropertyChanged -= handler;

  Assert.IsTrue(fired);
}
 
Now in order to run my test, I can write something like this as long as I’m not having multiple threads that change my object under test:
 
 
//create new user
User user = new User("Tim");

//make sure changing the name raises event
user.AssertFiresPropertyChangedEvent("Name", u => u.Name = "Tom");

 

The second extension method makes sure the event is fired a given number of times for a specified property. Apart from the obvious one (looping a few times), this one has additional use cases:

  • I can test that the event doesn’t fire at all by setting the expected number of events to 0.
  • This method ignores events for other properties, whereas the snippet above fails.

 

/// <summary>
/// Checks whether a given action produces a specified number of property
/// change events for a given property.<br/>
/// This event listener just ignores event for properties other than
/// <paramref name="propertyName"/>.
/// </summary>
/// <typeparam name="T">A class that implements <see cref="INotifyPropertyChanged"/>.
/// </typeparam>
/// <param name="host">The object that contains the changed property.</param>
/// <param name="numberOfEvents">The expected number of change events for
/// the specified property</param>
/// <param name="changeAction">An action that triggers the
/// <see cref="INotifyPropertyChanged.PropertyChanged"/> event.</param>
/// <param name="propertyName">The name of the changed property, as advertised by the
/// <see cref="INotifyPropertyChanged.PropertyChanged"/> event.</param>
public static void AssertPropertyChangedEventCount<T>(this T host, string propertyName,
                                                      int numberOfEvents,
                                                      Action<T> changeAction)
                                                      where T : INotifyPropertyChanged
{
  int eventCount = 0;

  //register handler with assertion
  PropertyChangedEventHandler handler = (sender, e) =>
    {
      //increment if the event refers to the tested property
      if (e.PropertyName == propertyName) eventCount++;
    };

  //register handler, trigger action, then deregister
  host.PropertyChanged += handler;
  changeAction(host);
  host.PropertyChanged -= handler;

  //compare counted events with expected number
  Assert.AreEqual(numberOfEvents, eventCount);
}

 

Update:

If you want to test a full set of properties of a class at once, check out Josh Twist’s ClassTester: This handy little library simplifies things remarkably when it comes to testing complete classes or even assemblies.

Author: Categories: C#, Testing Tags: