#1,148 – When to Use a Generic Covariant Interface

You can use a generic covariant interface as a workaround for the lack of covariance in generic types.

Suppose that we have a generic Pile<T> type as follows:

    public class Pile<T> where T: class
    {
        List<T> pile = new List<T>();

        public void Add(T item)
        {
            if (!pile.Contains(item))
                pile.Add(item);
        }

        public T GetFirst()
        {
            return (pile.Count > 0) ? pile[0] : null;
        }
    }

Because this generic type doesn’t support covariance, we can run into the problem shown below.

        static void Main(string[] args)
        {
            Pile<Terrier> lilDogs = new Pile<Terrier>();
            lilDogs.Add(new Terrier("Jack"));
            lilDogs.Add(new Terrier("Eddie"));

            // Compile error: can't convert from Pile<Terrier> to
            // Pile<Dog>
            ShowFirstDog(lilDogs);
        }

        static void ShowFirstDog(Pile<Dog> dogs)
        {
            Console.WriteLine(dogs.GetFirst().Name);
        }

We could write a method that took a Pile<Terrier>, rather than Pile<Dog>, but the it still might be useful to have a Pile<Dog> version that could handle various subtypes of Dog.

The workaround is to add an interface to Pile<T> that contains only methods that have T as an output parameter.  The interface marks the T parameter with the out keyword.  The updated code is shown below.

    public interface IGetFirst<out T>
    {
        T GetFirst();
    }

    public class Pile<T> : IGetFirst<T>
        where T: class
    {
        List<T> pile = new List<T>();

        public void Add(T item)
        {
            if (!pile.Contains(item))
                pile.Add(item);
        }

        public T GetFirst()
        {
            return (pile.Count > 0) ? pile[0] : null;
        }
    }

We can now use IGetFirst<T> covariantly, rather than trying to use Pile<T> covariantly.

        static void Main(string[] args)
        {
            Pile<Terrier> lilDogs = new Pile<Terrier>();
            lilDogs.Add(new Terrier("Jack"));
            lilDogs.Add(new Terrier("Eddie"));

            // This works !
            ShowFirstDog(lilDogs);
        }

        static void ShowFirstDog(IGetFirst<Dog> dogs)
        {
            Console.WriteLine(dogs.GetFirst().Name);
        }

1184-001

About Sean
Software developer in the Twin Cities area, passionate about software development and sailing.

One Response to #1,148 – When to Use a Generic Covariant Interface

  1. Pingback: Dew Drop – July 29, 2014 (#1824) | Morning Dew

Leave a comment