Mixing generics and polymorphism

Polymorphism, which attempts to hide differences in implementation, and generics, which attemtps to highlight them by providing exact information about types, don't seem to mix very well. Consider the following fairly common pattern.

        class Base
        {
        }

        class Derived : Base
        {
        }

        abstract class Manipulator
        {
            List<Base> list;

            public Manipulator(List<Base> list)
            {
                this.list = list;
            }
         }

        class BaseManipulator : Manipulator
        {
            public BaseManipulator(List<Base> list)
                : base(list)
            { }
        }

        class DerivedManipulator : Manipulator
        {
            public DerivedManipulator(List<Derived> list)
                : base(list)
            { }
        }

        public static void Main()
        {
            List<Derived> list = new List<Derived>();
            list.Add(new Derived());
            DerivedManipulator d = new DerivedManipulator(list);
        }

The code above will not compile - the compiler complains that List<Derived> cannot be converted to List<Base> in DerivedManipulator's constructor. Which seems kindof strange, given that Derived[] is implicitly convertible to Base[]. But what would happen if implicit conversion occurred with generics?

List<Derived> derivedList = new List<Derived>();
List<Base> baseList = derivedList;
baseList.Add(new Base());
Derived d = derivedList[0]; // BOOM

All hell will break loose. The type safety that generics offer will disappear, obviating the very need for generics.

What can be done then? We could make Manipulator a generic class, taking the type of the List as a generic parameter and make DerivedManipulator derive from Manipulator<Derived>. But then we are only shifting the problem - treating Manipulators polymorphically becomes impossible then.

There seems to be a basic impedance mismatch between polymorphism and generics. Does anyone know of a better way to solve the problem?

Comments

# re: Mixing generics and polymorphism

Monday, October 15, 2007 7:46 AM by Vivek Ragunathan

Will using the array IBase[] and IDerived[] not help you ?

# re: Mixing generics and polymorphism

Tuesday, October 16, 2007 9:05 AM by pjsson

class DerivedManipulator : Manipulator

{

   public DerivedManipulator(List<Base> list)

       : base(list)

   { }

}

public static void Main()

{

   List<Base> list = new List<Base>();

   list.Add(new Derived());

   DerivedManipulator d = new DerivedManipulator(list);

}

# re: Mixing generics and polymorphism

Thursday, October 18, 2007 11:40 AM by Senthil

pjsson : You lose typesafety with your approach. The whole point of using List<T> was to make sure that DerivedManipulator can take only a list of Derived instances.

# re: Mixing generics and polymorphism

Tuesday, January 15, 2008 11:15 AM by Bojan Jovanovic

You can use generic parameter for Manipulator which must inherit from Base. By specifying virtual method Merge in manipulator, you can organize merging two items hierarchycally. Here is complete code:

    abstract class Item { }

   class Base { }

   class Derived : Base { }

   abstract class Manipulator<TItem>

       where TItem : Item

   {

       List<TItem> list;

       public Manipulator(List<TItem> list)

       {

           this.list = list;

       }

       public virtual TItem Merge(TItem x, TItem y)

       { }

   }

   class BaseManipulator : Manipulator<Base>

   {

       public BaseManipulator(List<Base> list)

           : base(list)

       { }

   }

   class DerivedManipulator : Manipulator<Derived>

   {

       public DerivedManipulator(List<Derived> list)

           : base(list)

       { }

   }

# re: Mixing generics and polymorphism

Tuesday, January 15, 2008 12:46 PM by Senthil

Bojan,

Like I mentioned in the blog post, that only shifts the problem - with your code, it becomes impossible to treat Manipulators polymorphically. I can't do

Manipulator&lt;Base&gt; = new Manipulator&lt;Derived&gt;

for example.

# re: Mixing generics and polymorphism

Wednesday, April 02, 2008 12:39 AM by HS

I think this is what is known as the "generics variance" problem, which is a shortcoming of C#

see this:

msdn2.microsoft.com/.../ms228359(vs.80).aspx

in Java, you can declare the Manipulator class as follows:

Manipulator<? extends Base>

and all will work nicely after that.

Leave a Comment

(required) 
(required) 
(optional)
(required)