18

Complete question before code:

Why is IEnumerable<T> where T : ITest not accepted as receiver of an extension method that expects this IEnumerable<ITest>?

And now the code:

I have three types:

public interface ITest { }
public class Element : ITest { }
public class ElementInfo : ITest { }

And two extension methods:

public static class Extensions
{
    public static IEnumerable<ElementInfo> Method<T>(
        this IEnumerable<T> collection) 
        where T : ITest
    {
→        return collection.ToInfoObjects();
    }

    public static IEnumerable<ElementInfo> ToInfoObjects(
        this IEnumerable<ITest> collection)
    {
        return collection.Select(item => new ElementInfo());
    }
}

The compiler error I get (on the marked line):

CS1929 : 'IEnumerable<T>' does not contain a definition for 'ToInfoObjects' and the best extension method overload 'Extensions.ToInfoObjects(IEnumerable<ITest>)' requires a receiver of type 'IEnumerable<ITest>'

Why is this so? The receiver of the ToInfoObjects extension method is an IEnumerable<T> and by the generic type constraint, T must implement ITest.

Why is then the receiver not accepted? My guess is the covariance of the IEnumerable<T> but I am not sure.

If I change ToInfoObjects to receive IEnumerable<T> where T : ITest, then everything is ok.

Kornelije Petak
  • 9,412
  • 15
  • 68
  • 96

2 Answers2

14

Consider this:

public struct ValueElement : ITest { }

and this:

IEnumerable<ValueElement> collection = ...
collection.Method(); //OK, ValueElement implement ITest, as required.
collection.ToInfoObjects() //Error, IEnumerable<ValueElement> is not IEnumerable<ITest>
                           //variance does not work with value types.

So that not every type allowed for Method also allowed for ToInfoObjects. If you add class constraint to T in Method, then your code will compile.

user4003407
  • 21,204
  • 4
  • 50
  • 60
  • 1
    Now that you've said it, it seems obvious to me. T could have been the value type and of course it wouldn't work. Thank you very much. As a follow up, I would like to [link to the reason](http://stackoverflow.com/questions/12454794/why-covariance-and-contravariance-do-not-support-value-type) why co(ntra)variance does not work with value types. – Kornelije Petak Jan 29 '16 at 12:26
-1

You can do the following:

    public static IEnumerable<ElementInfo> Method<T>(
        this IEnumerable<T> collection)
        where T : ITest
    {
        return collection.ToInfoObjects();
    }

    public static IEnumerable<ElementInfo> ToInfoObjects<T>(
        this IEnumerable<T> collection)
    {
        return collection.Select(item => new ElementInfo());
    }

Notice on ToInfoObjects.

Chris Wohlert
  • 610
  • 3
  • 12
  • Thanks for your suggestion, but I know what I can do to solve the problem, and even more, I have written it in the original post. What I wanted is to know why the problem happens, not how to solve it. – Kornelije Petak Jan 29 '16 at 10:13
  • @KornelijePetak Oh, sorry for not paying enough attention to your question. – Chris Wohlert Jan 29 '16 at 10:28