2008-04-03

Factory methods in C# revisited

C#'s delegates and generics allow you to define factory methods as first-class citizens:

/// <summary>
/// A factory method creates instances of a given type.
/// </summary>
/// <typeparam name="T">The type of object created by the factory method.</typeparam>
/// <returns>A new instance of type <typeparamref name="T"/>.</returns>
/// <remarks><para>Factory methods may not return <langword name="null" /> or a reference to an existing object.</para></remarks>
public delegate T FactoryMethod<T>();
 
/// <summary>
/// Facility class for creating common factory methods.
/// </summary>
public static class FactoryMethods {
    /// <summary>
    /// Creates a factory method that creates new objects by invoking the default constructor.
    /// </summary>
    /// <typeparam name="T">The type of object created by the factory method.</typeparam>
    public static FactoryMethod<T> New<T>() where T : new() {
        return delegate() { return new T(); };
    }
 
    /// <summary>
    /// Creates a factory method that creates new objects by cloning a prototype.
    /// </summary>
    /// <typeparam name="T">The type of object created by the factory method. It must implement <see cref="ICloneable"/>.</typeparam>
    /// <param name="prototype">The instance to clone.</param>
    /// <exception cref="ArgumentNullException"><paramref name="prototype"/> is <langword name="null"/>.</exception>
    public static FactoryMethod<T> Clone<T>(T prototype) where T : ICloneable {
        if (prototype == null) throw new ArgumentNullException();
        return delegate() { return (T) prototype.Clone(); };
    }
 
    /// <summary>
    /// Creates a factory method that creates new objects by copying a value type.
    /// </summary>
    /// <typeparam name="T">The type of object created by the factory method. It must be a value type.</typeparam>
    /// <param name="prototype">The instance to copy.</param>
    public static FactoryMethod<T> Copy<T>(T prototype) where T : struct {
        return delegate() { return prototype; };
    }
}

You may be wondering about Copy<T> — where is it actually copying anything? The answer is that instances of value types are copied when they're assigned, and that includes implicit assignments like passing them as arguments and returning them.

This is a good example of code that looks very neat but probably has few practical applications: when I use factory methods, they usually have parameters.

The idea behind it is sound enough, though. You can easily declare your own delegate type for those scenarios. Of course, the other aspect of factory methods is that they're usually overridable methods, to support polymorphic creation of objects from a base class. A delegate is unnecessarily flexible in this case.

No comments: