Следующему коду не удается скомпилировать (использующий VS2010), и я не вижу почему. Компилятор должен смочь вывести это List<TestClass>
'совместимо' (извините из-за отсутствия лучшего слова) с IEnumerable<ITest>
, но так или иначе это не делает. Что я пропускаю здесь?
interface ITest {
void Test();
}
class TestClass : ITest {
public void Test() {
}
}
class Program {
static void Test(IEnumerable<ITest> tests) {
foreach(var t in tests) {
Console.WriteLine(t);
}
}
static void Main(string[] args) {
var lst = new List<TestClass>();
Test(lst); // fails, why?
Test(lst.Select(t=>t as ITest)); //success
Test(lst.ToArray()); // success
}
}
Компилятор дает две ошибки:
Лучший перегруженный метод соответствует для 'ConsoleApplication1. Программа. Тест (Система. Наборы. Универсальный. IEnumerable <ConsoleApplication2. ITest>)', имеет некоторые недействительные аргументы
Аргумент 1: не может преобразовать из 'Системы. Наборы. Универсальный. Список <ConsoleApplication2. TestClass>' к 'Системе. Наборы. Универсальный. IEnumerable <ConsoleApplication2. ITest>'
То, что вы пытаетесь сделать, называется ковариацией - преобразование из более узкого типа (TestClass) в более широкий тип (ITest). Это то, к чему вы будете постоянно привыкать, это происходит, например, когда вы конвертируете из числа с плавающей запятой в двойное.
К сожалению .Net 3.5 и ниже не поддерживает ковариацию в общих классах.
.Net 4.0 теперь поддерживает ковариацию (и контравариантность) в универсальных типах, при условии, что эти универсальные классы скомпилированы с ключевыми словами out
для ковариантных типов и в
для контравариантных типов. IEnumerable
в .Net 4.0 определяется как ковариантный. Если вы щелкните правой кнопкой мыши тип IEnumerable
и выберите «перейти к определению», вы увидите следующее:
public interface IEnumerable<out T> : IEnumerable
Если вы используете VS2010, вам нужно будет убедиться, что ваш проект нацелен на .net 4.0. Это можно изменить в свойствах проекта. Щелкните правой кнопкой мыши проект, выберите свойства, перейдите на вкладку «Приложение» и убедитесь, что «Целевая платформа» соответствует .Net 4.
Это связано с дисперсией (ковариацией и контравариацией); посмотрите этот пост и ответ Jon Skeet
Проверьте целевую версию платформы для вашего проекта. Этот код будет работать только в .NET 4.