C# Variance Issue with IEnumerable<T> vs <T>

So, I'm having an issue with similar code to below:

public static String MyFunc<T>(this IEnumerable<T> list) where T : struct
{
    ... some code ...
    return myString;
}

public static String MyFunc<T>(this T o) where T : struct
{
    ... some code ...
    return myString;
}

The problem is that when trying to do call MyFunc on a List it uses the second function instead of the one that accepts an IEnumerable. I know this has to do with variance, but I'm uncertain as to how to force it to use the first function rather than the second. The code I would use to call the first one would be:

List<int> x = new List<int>();
String s = x.MyFunc();

The above code immediately goes to the second function and I need it to use the first. How can I force the desired behavior? Incidentally, I'm using .NET 4.0

Jon Skeet
people
quotationmark

The reason that it's currently picking the second method is that a conversion from a type to itself (second method, T=List<int>, conversion from List<int> to List<int>) will always be "better" than a conversion to the type it implements (first method, T=int, conversion from List<int> to IEnumerable<int>). This has nothing to do with variance, by the way - it's just the method overloading algorithm and type inference.

Note that with your current code, although the second overload is picked, it will then be found to be invalid because T violates the T : struct constraint. The constraint is only checked after the overload is chosen. See Eric Lippert's blog post on this for more details.

I suggest you just give the two methods different names.

EDIT: As noted by Anthony in comments, this can work if you call it as:

x.AsEnumerable().MyFunc();

Or just change the declaration to:

IEnumerable<int> x = new List<int>();
x.MyFunc();

It's not entirely clear to me exactly why it's better here - in this case after type argument substitution, you've basically got IEnumerable<T> as the parameter type in both cases. However, I would still strongly recommend using different names here. The fact that it's got me puzzling over the spec to work out which overload is being called should be enough indication that the behaviour won't be immediately clear to everyone reading the code.

EDIT: I think the reason is here (from the C# 5 spec, section 7.5.3.2):

  • A type parameter is less specific than a non-type parameter

So just T is less specific than IEnumerable<T>, even though the latter involves a type parameter. It's still not clear to me whether this is the language designers' intended behaviour... I can see why a type which involves type parameters should be seen as less specific than a type which doesn't involve type parameters, but not quite this wording...

people

See more on this question at Stackoverflow