Проблема дисперсии в 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>
даже если последний включает параметр типа. Мне все еще не ясно, является ли это предполагаемым поведением разработчиков языка... Я понимаю, почему тип, который включает параметры типа, должен рассматриваться как менее специфичный, чем тип, который не включает параметры типа, но не совсем эту формулировку...