As you can see, I have a static generic method Something with an IEnumerable<T> parameter (and T is constrained to be an IA interface), and this parameter can't be implicitly converted to IEnumerable<IA>.

What is the explanation? (I don't search for a workaround, just to understand why it doesn't work).

You have not restricted T to reference types. If you use the condition where T: class, IA then it should work. The linked answer has more details.
– DirkNov 7 at 8:41

2

@Dirk I don't think this should be flagged as a duplicate. While it's true that the concept problem here is a covariance/contravariance problem in the face of value types, the specific case here is "what does this error message mean" as well as the author not realizing merely including "class" fixes his issue. I believe future users will search for this error message, find this post, and leave happy. (As I often do.)
– Reginald BlueNov 7 at 13:46

You can also reproduce the situation by simply saying Something2(foo); directly. Going around .ToList() to get a List<T> (T is your type parameter declared by the generic method) is not needed to understand this (a List<T> is an IEnumerable<T>).
– Jeppe Stig NielsenNov 8 at 12:18

@ReginaldBlue 100%, was going to post the same thing. Similar answers do not a duplicate question make.
– DaveInCazNov 11 at 13:35

2 Answers
2

The error message is insufficiently informative, and that is my fault. Sorry about that.

The problem you are experiencing is a consequence of the fact that covariance only works on reference types.

You're probably saying "but IA is a reference type" right now. Yes, it is. But you didn't say that Tis equal toIA. You said that T is a type which implementsIA, and a value type can implement an interface. Therefore we do not know whether covariance will work, and we disallow it.

If you want covariance to work you have to tell the compiler that the type parameter is a reference type with the class constraint as well as the IA interface constraint.

The error message really should say that the conversion is not possible because covariance requires a guarantee of reference-type-ness, since that is the fundamental problem.

@user4951: Because I implemented all of the conversion checking logic including the error messages.
– Eric LippertNov 7 at 15:06

2

@PeterA.Schneider: I appreciate that. But one of my primary goals for designing the error reporting logic in Roslyn was in particular to capture not just what rule was violated, but furthermore, to identify the "root cause" where possible. For example, what should the error message be for customers.Select(c=>c.FristName) ? The C# specification is very clear that this is an overload resolution error: the set of applicable methods named Select that can take that lambda is empty. But the root cause is that FirstName has a typo.
– Eric LippertNov 8 at 19:39

3

@PeterA.Schneider: I did a lot of work to ensure that scenarios involving generic type inference and lambdas used the appropriate heuristics to deduce what error message was likely to best help the developer. But I did a far less good job on the conversion error messages, particularly where variance was concerned. I've always regretted that.
– Eric LippertNov 8 at 19:40

4

@EricLippert This is probably not the place to say this but that is why I (and many more like me) always look up to you Eric (always loved the way you explain), thank you for all the hard work and enlightenment and counting++. My sign of respect/appreciation for one of the best in the business :)
– Rohit416Nov 13 at 11:46

I'm curious... what exactly is the reason behind the significance of the ordering?
– Tom WrightNov 8 at 2:11

5

@TomWright - the spec doesn't, of course, include the answer to many "Why?" questions, but in this case does make it clear that there are three distinct types of constraints, and when all three are used they have to be specifically primary_constraint ',' secondary_constraints ',' constructor_constraint
– Damien_The_UnbelieverNov 8 at 7:05

2

@TomWright: Damien is correct; there is no particular reason I'm aware of other than the convenience of the author of the parser. If I had my druthers the syntax for type constraints would be considerably more verbose. class is bad because it means "reference type", not "class". I would have been happier with something verbose like where T is not struct
– Eric LippertNov 10 at 0:49