Проблема дисперсии в C# с IEnumerable<T> против <T>

Итак, у меня проблема с кодом, подобным приведенному ниже:

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;
}

Проблема заключается в том, что при попытке вызвать MyFunc для списка он использует вторую функцию вместо той, которая принимает IEnumerable. Я знаю, что это связано с дисперсией, но я не уверен, как заставить его использовать первую функцию, а не вторую. Код, который я использовал бы для вызова первого:

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

Приведенный выше код сразу переходит ко второй функции, и мне нужно использовать первую. Как я могу заставить желаемое поведение? Я, кстати, использую.NET 4.0

1 ответ

Причина, по которой в настоящее время выбирается второй метод, заключается в том, что преобразование из типа в самого себя (второй метод, T=List<int>, преобразование из List<int> в List<int>) всегда будет "лучше", чем преобразование в тип, который оно реализует (первый метод, T=int, преобразование из List<int> в IEnumerable<int>). Кстати, это не имеет ничего общего с дисперсией - это просто алгоритм перегрузки метода и вывод типа.

Обратите внимание, что с вашим текущим кодом, хотя и выбрана вторая перегрузка, он будет признан недействительным, потому что T нарушает T : struct ограничение. Ограничение проверяется только после выбора перегрузки. См. Сообщение в блоге Эрика Липперта об этом для получения более подробной информации.

Я предлагаю вам дать двум методам разные имена.

РЕДАКТИРОВАТЬ: Как отметил Энтони в комментариях, это может работать, если вы называете это как:

x.AsEnumerable().MyFunc();

Или просто измените объявление на:

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

Мне не совсем понятно, почему именно здесь лучше - в этом случае после подстановки аргумента типа вы в основном получаете IEnumerable<T> в качестве типа параметра в обоих случаях. Тем не менее, я все равно настоятельно рекомендую использовать здесь разные имена. Тот факт, что я ломаю голову над спецификацией, чтобы решить, какая перегрузка вызывается, должен быть достаточным признаком того, что поведение не будет сразу понятно всем, кто читает код.

РЕДАКТИРОВАТЬ: Я думаю, что причина здесь (из спецификации C# 5, раздел 7.5.3.2):

  • Параметр типа менее специфичен, чем параметр без типа

Так просто T менее конкретен, чем IEnumerable<T>даже если последний включает параметр типа. Мне все еще не ясно, является ли это предполагаемым поведением разработчиков языка... Я понимаю, почему тип, который включает параметры типа, должен рассматриваться как менее специфичный, чем тип, который не включает параметры типа, но не совсем эту формулировку...

Другие вопросы по тегам