список C# Linq.Метод AddRange Не Работает
у меня есть интерфейс, как указано ниже:
public interface TestInterface{
int id { get; set; }
}
и два класса Linq-to-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)
есть ли что-то, что я делаю неправильно? Спасибо за любую помощь
4 ответов
это работает в C# 4, из-за общая ковариантность. В отличие от предыдущих версий C#, существует преобразование из IEnumerable<tblTestA>
до IEnumerable<TestInterface>
.
функциональность была в CLR от v2, но она была представлена только в C# 4 (и типы фреймворков не использовали ее до .NET 4). Это только применяется к общим интерфейсам и делегатам (не классам) и только для ссылочных типов (поэтому нет преобразования из IEnumerable<int>
to IEnumerable<object>
например.) Работает только там, где это имеет смысл - IEnumerable<T>
является ковариантным, поскольку объекты только "выходят" из API, тогда как IList<T>
is инвариант потому что вы также можете добавлять значения с этим API.
Generic contravariance также поддерживается, работает в другом направлении-так, например, вы можете конвертировать из IComparer<object>
to IComparer<string>
.
если вы не используете C# 4, то предложение Тима использовать Enumerable.Cast<T>
хороший - вы теряете немного эффективности, но это будет работать.
если вы хотите узнать больше об общей дисперсии, у Эрика Липперта есть длинная серия сообщений в блоге об этом, и я говорил об этом в NDC 2010, который вы можете посмотреть на страница видео NDC.
вы не делаете ничего плохого:List<TestInterface>.AddRange
ждет IEnumerable<TestInterface>
. Он не примет IEnumerable<tblTestA>
или IEnumerable<tblTestB>
.
код foreach
циклы работы. Кроме того, вы можете использовать Cast
для изменения типа:
List<TestInterface> list = new List<TestInterface>();
list.AddRange(a.Cast<TestInterface>());
list.AddRange(b.Cast<TestInterface>());
AddRange ожидает список объектов интерфейса, а ваши" A " и " b " varaibles определены как список объектов производного класса. Очевидно, кажется разумным, что .NET может логически сделать этот прыжок и рассматривать их как списки объектов интерфейса, потому что они действительно реализуют интерфейс, эта логика просто не была встроена в .NET через 3.5.
однако эта способность (называемая "ковариация") была добавлена в .NET 4.0, но до тех пор, пока вы не обновите ее, вы застряли в цикле или, возможно, попытаетесь вызвать ToArray (), а затем передать результат в TaskInterface [] или, возможно, запрос LINQ для каждого элемента и создания нового списка и т. д.
a
и b
типа IEnumerable<tblTestA>
и IEnumerable<tblTestB>
В то время как list.AddRange
требует параметр типа IEnumerable<TestInterface>