Limitations of generic base classes


This post was originally published here.

I thought I had created something fairly useful with a generic Value Object in a previous post.  Generic base classes are nice, and there are several recommended base classes for creating collections classes.  Whenever I try to make an interesting API with a generic base class, limitations and faulty assumptions always reduce the usefulness of that base class.  Let’s first start with a couple of classes that we’d like to include in a generic API:

public class Address
{
    private readonly string _address1;
    private readonly string _city;

    public Address(string address1, string city)
    {
        _address1 = address1;
        _city = city;
    }

    public string Address1
    {
        get { return _address1; }
    }

    public string City
    {
        get { return _city; }
    }
}

public class ExpandedAddress : Address
{
    private readonly string _address2;

    public ExpandedAddress(string address1, string address2, string city)
        : base(address1, city)
    {
        _address2 = address2;
    }

    public string Address2
    {
        get { return _address2; }
    }
}

Fairly basic classes, just two types of addresses, with one a subtype of the other.  So what kinds of issues do I usually run in to with generic base classes?  Let’s look at a few different types of generic base classes to see.

Concrete generic implementations

A concrete generic implementation is a concrete class that inherits a generic base class:

public class AddressCollection : Collection<Address>
{
    public AddressCollection() {}

    public AddressCollection(IList<Address> list) : base(list) {}
}

Following the Framework Design Guidelines, I created a concrete collections class by inheriting from System.Collections.ObjectModel.Collection.  I also have the ExpandedAddress class, so how do I create a specialized collection of ExpandedAddresses?  I have a few options:

  • Create an ExpandedAddressCollection class inheriting from AddressCollection
    • Create an ExpandedAddressCollection class inheriting from Collection

      • Use the existing AddressCollection class and put ExpandedAddress instances in it.
      All of these seem reasonable, right?  Let’s take a closer look.

    Inherit from AddressCollection

    Here’s what the ExpandedAddressCollection class would look like:

    public class ExpandedAddressCollection : AddressCollection

{
    public ExpandedAddressCollection() {}

    public ExpandedAddressCollection(IList<Address> list) : base(list) {}
}

    That&#8217;s not very interesting, it didn&#8217;t add any information to the original AddressCollection.&nbsp; What&#8217;s more, this ExpandedAddressCollection ultimately inherits Collection<Address>, _not_ Collection<ExpandedAddress>.&nbsp; Everything I try to put in or get out will be an Address, not an ExpandedAddress.&nbsp; For example, this code wouldn&#8217;t compile:
    
    <div class="CodeFormatContainer">
      <pre>List&lt;ExpandedAddress&gt; addresses = <span class="kwrd">new</span> List&lt;ExpandedAddress&gt;();<br />

addresses.Add(new ExpandedAddress(“Address1”, “Austin”, “TX”));

ExpandedAddressCollection addressList = new ExpandedAddressCollection(addresses);

    Because of limitations in generic variance, namely that C# [does not support](http://msdn2.microsoft.com/en-us/library/ms228359(vs.80).aspx) generic [covariance or contravariance](http://blogs.msdn.com/rmbyers/archive/2005/02/16/375079.aspx).&nbsp; Even though ExpandedAddress&nbsp;is a subtype of&nbsp;Address, and ExpandedAddress[] is a subtype of Address[],&nbsp;IList<ExpandedAddress> **is not** a subtype of IList<Address>.
    
    #### Inherit from Collection<ExpandedAddress>
    
    In this example, I&#8217;ll just implement the ExpandedAddressCollection in the same manner as AddressCollection:
    
    <div class="CodeFormatContainer">
      <pre><span class="kwrd">public</span> <span class="kwrd">class</span> ExpandedAddressCollection : Collection&lt;ExpandedAddress&gt;<br />

{
    public ExpandedAddressCollection() {}

    public ExpandedAddressCollection(IList<ExpandedAddress> list) : base(list) { }
}

    Now I my collection is strongly types towards ExpandedAddresses, so the example I showed previously would now compile.&nbsp; It seems like I&#8217;m on the right track, but I run into even more issues:
    
      * ExpandedAddressCollection is not a subtype of AddressCollection 
          * Collection hierarchy does not match hierarchy of Addresses (one is a tree, the other is flat) 
              * I can&#8217;t pass an ExpandedAddressCollection into a method expecting an AddressCollection 
                  * Since there is no relationship between the two collection types, I can&#8217;t use many patterns where a relationship is necessary</ul> 
                So even though my collection is strongly typed, it becomes severely limited in more interesting scenarios.
                
                #### Use existing AddressCollection class
                
                In this instance, I won&#8217;t even create an ExpandedAddressCollection class.&nbsp; Any time I need a collection of ExpandedAddresses, I&#8217;ll use the AddressCollection class, and cast as necessary.&nbsp; I won&#8217;t be able to pass an IList<ExpandedAddress> to the constructor because of the variance issue, however.&nbsp; If I need to include some custom logic in the collection class, I&#8217;ll run into the same problems highlighted earlier if I&#8217;m forced to create a new subtype of AddressCollection.
                
                So we&#8217;ve seen the limitations of concrete generic implementations, what other options do I have?
                
                ### Constrained generic base classes
                
                I&#8217;d like a way to propagate the concrete type parameter&nbsp;back up to the original Collection<T>.&nbsp; What if I make the AddressCollection generic as well?&nbsp; Here&#8217;s what that would look like:
                
                <div class="CodeFormatContainer">
                  <pre><span class="kwrd">public</span> <span class="kwrd">class</span> AddressCollection&lt;T&gt; : Collection&lt;T&gt;<br />

    where T : Address
{
    public AddressCollection() {}

    public AddressCollection(IList<T> list) : base(list) {}
}

public class ExpandedAddressCollection : AddressCollection<ExpandedAddress>
{
    public ExpandedAddressCollection() {}

    public ExpandedAddressCollection(IList<ExpandedAddress> list) : base(list) { }
}

                So now I have a constrained base class for an AddressCollection, and an implementation for an ExpandedAddressCollection.&nbsp; What do I gain from this implementation?
                
                  * ExpandedAddressCollection is completely optional, I could just define all usage through an AddressCollection<ExpandedAddress> 
                      * Any AddressCollection concrete type will be correctly strongly typed for an Address type</ul> 
                    Again, with some more interesting usage, I start to run into some problems:
                    
                      * I can never reference only AddressCollection, as I always have to give it a type parameter. 
                          * Once I give it a type parameter, I run into the same variance issues as before, namely AddressCollection<ExpandedAddress> is not a subtype of AddressCollection<Address> 
                              * Since I can never define any method in terms of solely an AddressCollection, I either need to make the containing class generic or the method generic.</ul> 
                            For example, I can write the following code:
                            
                            <div class="CodeFormatContainer">
                              <pre><span class="kwrd">public</span> <span class="kwrd">void</span> TestGenerics()<br />

{
    AddressCollection<Address> addresses = new AddressCollection<Address>();
    addresses.Add(new Address(“132 Anywhere”, “Austin”));

    int count = NumberOfAustinAddresses(addresses);

    AddressCollection<ExpandedAddress> expAddresses = new AddressCollection<ExpandedAddress>();
    expAddresses.Add(new ExpandedAddress(“132 Anywhere”, “Apt 123”, “Austin”));

    count = NumberOfAustinAddresses(addresses);
}

private int NumberOfAustinAddresses<T>(AddressCollection<T> addresses)
    where T : Address
{
    int count = 0;
    foreach (T address in addresses)
    {
        if (address.City == “Austin”)
            count++;
    }
    return count;
}

                            This isn&#8217;t too bad for the implementation nor the client code.&nbsp; I don&#8217;t even need to specify a generic parameter in the method calls.&nbsp; If I can live with generic methods, this pattern might work for most situations.
                            
                            The only other problem I&#8217;ll have is that I might need to create subtypes of AddressCollection, like I did above with ExpandedAddressCollection.&nbsp; In this case, I&#8217;d can continue to make each subtype generic and constrained to the derived type:
                            
                            <div class="CodeFormatContainer">
                              <pre><span class="kwrd">public</span> <span class="kwrd">class</span> ExpandedAddressCollection&lt;T&gt; : AddressCollection&lt;T&gt;<br />

    where T : ExpandedAddress
{
    public ExpandedAddressCollection() {}

    public ExpandedAddressCollection(IList<T> list) : base(list) { }
}

                            Again, if I can live with generic methods, I would be happy with this implementation, as I now keep the same hierarchy as my Addresses.&nbsp; It is a little strange declaring ExpandedAddressCollection with a type parameter (ExpandedAddressCollection<ExpandedAddress>), but I&#8217;ll live.
                            
                            There&#8217;s one more type of generic base class I&#8217;d like to explore.
                            
                            ### Self-constrained generic base classes
                            
                            Sometimes, I need to implement a certain generic interface, such as IEquatable<T>.&nbsp; I could&nbsp;simply pass in the concrete type into the generic parameter like this:
                            
                            <div class="CodeFormatContainer">
                              <pre><span class="kwrd">public</span> <span class="kwrd">abstract</span> <span class="kwrd">class</span> ValueObject : IEquatable&lt;ValueObject&gt;<br />

                            But if I&#8217;m trying to create an abstract or a base class for subtypes to use, I&#8217;ll run into problems where derived types won&#8217;t implement IEquatable<DerivedType>.&nbsp; Instead, I can make this abstract class generic and self-constraining:
                            
                            <div class="CodeFormatContainer">
                              <pre><span class="kwrd">public</span> <span class="kwrd">abstract</span> <span class="kwrd">class</span> ValueObject&lt;T&gt; : IEquatable&lt;T&gt;<br />

    where T : ValueObject<T>

                            Now, derived types will implement IEquatable<DerivedType>, as I showed in my [post on value types](https://lostechies.com/blogs/jimmy_bogard/archive/2007/06/25/generic-value-object-equality.aspx).&nbsp; Unfortunately, subtypes will only implement the correct IEquatable<T> for the first derived class:
                            
                            <div class="CodeFormatContainer">
                              <pre><span class="kwrd">public</span> <span class="kwrd">class</span> Address : ValueObject&lt;Address&gt;<br />

public class ExpandedAddress : Address

                            In this case, ExpandedAddress is not a subtype of ValueObject<ExpandedAddress>, and therefore only implements IEquatable<Address>.&nbsp; I can&#8217;t use the same tricks with the constrained generic base class, as I would need to declare Address as generic, and therefore never instantiable by itself.&nbsp; The self-constrained generic base or abstract class is unfortunately only useful in hierarchies one level deep.
                            
                            ### Conclusion
                            
                            So generics aren&#8217;t the silver spoon I thought it would be, but there are some [interesting proposals to allow variance](http://research.microsoft.com/~akenn/generics/ECOOP06.pdf) for generic types out there.&nbsp; I might not be able to cover all of the scenarios I&#8217;d like for a generic base class, but by identifying several options and their consequences, I can make a better decision&nbsp;on solving&nbsp;the problem.
← ReSharper plugin for cyclomatic complexity
Refining daily stand-ups →