views:

105

answers:

3

The following code fails to compile (using VS2010) and I don't see why. The compiler should be able to infer that List<TestClass> is 'compatible' (sorry for lack of a better word) with IEnumerable<ITest>, but somehow it doesn't. What am I missing here?


interface ITest {
    void Test();
}


class TestClass : ITest {
    public void Test() {
    }
}

class Program {
    static void Test(IEnumerable<ITest> tests) {
        foreach(var t in tests) {
            Console.WriteLine(t);
        }
    }
    static void Main(string[] args) {
        var lst = new List<TestClass>();

        Test(lst); // fails, why?

        Test(lst.Select(t=>t as ITest)); //success

        Test(lst.ToArray()); // success
    }
}

The compiler gives two errors:

  1. The best overloaded method match for 'ConsoleApplication1.Program.Test(System.Collections.Generic.IEnumerable<ConsoleApplication2.ITest>)' has some invalid arguments

  2. Argument 1: cannot convert from 'System.Collections.Generic.List<ConsoleApplication2.TestClass>' to 'System.Collections.Generic.IEnumerable<ConsoleApplication2.ITest>'

+2  A: 

This has to do with variance (covariance and contravariance); check out this post and the answer by Jon Skeet

Thomas
+1  A: 

Check the Target Version of the framework for your project. This code will only work in .NET 4 .

Joel Cunningham
+6  A: 

What you are trying to do is called covariance - converting from a narrower type (TestClass) to a wider type (ITest). It's something you'll be used to all the time, it happens when you convert from a float to a double for example.

Unfortunately .Net 3.5 and lower does not support covariance in generic classes.

.Net 4.0 now does support covariance (and contravariance) in generics, provided those generic classes are compiled with the keywords out for covariant types and in for contravarient types. IEnumerable in .Net 4.0 is defined as covariant. If you right click on the IEnumerable type and click "goto definition", you'll see this:

public interface IEnumerable<out T> : IEnumerable

If you are using VS2010, you will need to make sure your project is targeting .net 4.0. This can be changed from the project properties. Right click on the project, select properties, goto the "Application" tab and check that the "Target framework" is to to .Net 4.

MSDN has more information.

Simon P Stevens