Мне определили интерфейс как указано ниже:
public interface TestInterface{
int id { get; set; }
}
И два класса Linq-SQL, реализовывая тот интерфейс:
public class tblTestA : TestInterface{
public int id { get; set; }
}
public class tblTestB : TestInterface{
public int id { get; set; }
}
У меня есть списки IEnumerable a и b, заполненный записями базы данных от tblTestA и tblTestB
IEnumerable<tblTestA> a = db.tblTestAs.AsEnumerable();
IEnumerable<tblTestB> b = db.tblTestBs.AsEnumerable();
Однако следующее не разрешено:
List<TestInterface> list = new List<TestInterface>();
list.AddRange(a);
list.AddRange(b);
Я должен сделать следующим образом:
foreach(tblTestA item in a)
list.Add(item)
foreach(tblTestB item in b)
list.Add(item)
Есть ли что-то, что я делаю неправильно? Спасибо за любую справку
Это работает в C # 4 из-за общей ковариации . В отличие от предыдущих версий C #, существует преобразование из IEnumerable
в IEnumerable
.
Функциональность была в CLR из v2, но она была представлена только в C # 4 (и типы фреймворков не использовали ее до .NET 4). Он только применяется к универсальным интерфейсам и делегатам (не классам) и только для ссылочных типов (поэтому нет преобразования из IEnumerable
в IEnumerable
Например.) Он также работает только там, где это имеет смысл - IEnumerable
ковариантен, поскольку объекты только «выходят» из API, тогда как IList
- ] инвариант , потому что вы также можете добавлять значения с помощью этого API.
Также поддерживается общая контравариантность, работающая в другом направлении - например, вы можете преобразовать из IComparer
в IComparer
.
Если вы не используете C # 4, то предложение Тима об использовании Enumerable.Cast
является хорошим - вы немного теряете эффективность, но оно будет работать.
Если вы хотите узнать больше об общей дисперсии, у Эрика Липперта есть длинная серия сообщений об этом в блоге , и я рассказал об этом на NDC 2010, который вы можете посмотреть на Видеостраница НДЦ .
Вы не делаете ничего неправильно: List
expects an IEnumerable
. Он не принимает IEnumerable
или IEnumerable
.
Ваши циклы foreach
работают. В качестве альтернативы вы можете использовать Cast
для изменения типов:
List<TestInterface> list = new List<TestInterface>();
list.AddRange(a.Cast<TestInterface>());
list.AddRange(b.Cast<TestInterface>());
AddRange ожидает список объектов интерфейса, а ваши вараибли "a" и "b" определены как список объектов производных классов. Очевидно, кажется разумным, что .NET может логически сделать этот переход и рассматривать их как списки объектов интерфейса, потому что они действительно реализуют интерфейс, просто эта логика не была встроена в .NET до версии 3.5.
Однако эта возможность (называемая "ковариацией") была добавлена в .NET 4.0, но пока вы не перейдете на нее, вы будете вынуждены использовать циклы, или, может быть, попытаетесь вызвать ToArray() и затем привести результат к TaskInterface[], или, может быть, LINQ-запрос для обработки каждого элемента и создания нового списка, и т. д.
a
и b
относятся к типу IEnumerable
и IEnumerable
, а list.AddRange
требует параметра для иметь тип IEnumerable