5

I'm using C#.NET 3.5. I get

argument type 'GenericTest.BarkStrategy' is not assignible to parameter type 'GenericsTest.IAnimalStrategy'

with the following (for this question as much simplified as possible) code:

using System.Collections.Generic;

namespace GenericsTest {
    class Program {
        static void Main(string[] args) {
            List<IAnimalStrategy<IAnimal>> strategies = 
                new List<IAnimalStrategy<IAnimal>>();
            strategies.Add(new BarkStrategy());
        }
    }

    interface IAnimal { }
    interface IAnimalStrategy<T> where T : IAnimal { }
    class Dog : IAnimal { }
    class BarkStrategy : IAnimalStrategy<Dog> { }
}
Ruudjah
  • 807
  • 7
  • 27
  • This problem has basically been answered in [this question](http://stackoverflow.com/questions/14621564/why-cant-a-list-of-an-interface-type-accept-intances-of-an-inheriting-interface). – O. R. Mapper Oct 23 '13 at 12:34

1 Answers1

14

You have to tell the compiler that your interface is covariant : IAnimalStrategy<out T>

namespace GenericsTest
{
    class Program
    {
        static void Main(string[] args)
        {
            List<IAnimalStrategy<IAnimal>> strategies = new List<IAnimalStrategy<IAnimal>>();
            strategies.Add(new BarkStrategy());
        }
    }

    interface IAnimal { }
    interface IAnimalStrategy<out T> where T : IAnimal { }

    class Dog : IAnimal { }
    class BarkStrategy : IAnimalStrategy<Dog> { }
}

Unfortunately, it is available only in C# 4.0 : How is Generic Covariance & Contra-variance Implemented in C# 4.0?

For understanding the problem, you can forget the list, this line doesn't compile :

IAnimalStrategy<IAnimal> s = new BarkStrategy();

The IAnimalStrategy<IAnimal> interface can do things on IAnimal, maybe set a property of type IAnimal

interface IAnimalStrategy<T> where T : IAnimal 
{
    T Animal {get; set;}
}

Then you would be able to do something like

IAnimalStrategy<IAnimal> s = new BarkStrategy();
s.Animal = new Cat();

It will blow in your face. So C#3.5 doesn't allow you to do that.
C#4.0 will allow you to that, if you said that T is covariant with the out keyword

interface IAnimalStrategy<out T> where T : IAnimal 
{
    T Animal {get; set;}
}

This will blow again,

Invalid variance: The type parameter 'T' must be invariantly valid on IAnimalStrategy.Animal'. 'T' is covariant.

Covariance and Contravariance are difficults to understand, I suggest you read the awesome series on Eric Lippert's Blog : Covariance and Contravariance in C#

James
  • 270
  • 1
  • 10
Cyril Gandon
  • 16,830
  • 14
  • 78
  • 122