c# Linq 'Список <Интерфейс>.AddRange' Метод, Не Работающий

Мне определили интерфейс как указано ниже:

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)

Есть ли что-то, что я делаю неправильно? Спасибо за любую справку

7
задан Jimbo 14 July 2010 в 09:42
поделиться

4 ответа

Это работает в 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, который вы можете посмотреть на Видеостраница НДЦ .

9
ответ дан 6 December 2019 в 12:46
поделиться

Вы не делаете ничего неправильно: List.AddRange expects an IEnumerable. Он не принимает IEnumerable или IEnumerable.

Ваши циклы foreach работают. В качестве альтернативы вы можете использовать Cast для изменения типов:

List<TestInterface> list = new List<TestInterface>();
list.AddRange(a.Cast<TestInterface>());
list.AddRange(b.Cast<TestInterface>());
6
ответ дан 6 December 2019 в 12:46
поделиться

AddRange ожидает список объектов интерфейса, а ваши вараибли "a" и "b" определены как список объектов производных классов. Очевидно, кажется разумным, что .NET может логически сделать этот переход и рассматривать их как списки объектов интерфейса, потому что они действительно реализуют интерфейс, просто эта логика не была встроена в .NET до версии 3.5.

Однако эта возможность (называемая "ковариацией") была добавлена в .NET 4.0, но пока вы не перейдете на нее, вы будете вынуждены использовать циклы, или, может быть, попытаетесь вызвать ToArray() и затем привести результат к TaskInterface[], или, может быть, LINQ-запрос для обработки каждого элемента и создания нового списка, и т. д.

1
ответ дан 6 December 2019 в 12:46
поделиться

a и b относятся к типу IEnumerable и IEnumerable
, а list.AddRange требует параметра для иметь тип IEnumerable

0
ответ дан 6 December 2019 в 12:46
поделиться
Другие вопросы по тегам:

Похожие вопросы: