Вопрос

У меня есть интерфейс, определенный ниже:

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)

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

Это было полезно?

Решение

Это работает в C# 4 из-за общая ковариация.В отличие от предыдущих версий C#, здесь имеется преобразование из IEnumerable<tblTestA> к IEnumerable<TestInterface>.

Эта функциональность присутствовала в CLR начиная с версии 2, но была представлена ​​только в C# 4 (и типы платформ не использовали ее до .NET 4).Это только применяется к универсальным интерфейсам и делегатам (не к классам) и только к ссылочным типам (поэтому преобразование из IEnumerable<int> к IEnumerable<object> например.) Это также работает только там, где это имеет смысл - IEnumerable<T> является ковариантным, поскольку объекты выходят только из API, тогда как IList<T> является инвариант потому что вы также можете добавлять значения с помощью этого API.

Также поддерживается общая контравариантность, работающая в другом направлении - например, вы можете конвертировать из IComparer<object> к IComparer<string>.

Если вы не используете C# 4, то предложение Тима об использовании Enumerable.Cast<T> хороший вариант — вы потеряете немного эффективности, но он сработает.

Если вы хотите узнать больше об общей дисперсии, у Эрика Липперта есть длинная серия сообщений в блоге об этом, и я выступил с докладом об этом на NDC 2010, который вы можете посмотреть на Страница видео НДЦ.

Другие советы

Вы не делаете ничего плохого: 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» варианта величин определены как список производных объектов класса.Очевидно, кажется разумным, что .NET может логически сделать это прыгать и относиться к ним в виде списков объектов интерфейса, потому что они действительно реализуют интерфейс, что логика была просто не создана в .NET до 3,5.

Однако эта способность (называемая «ковариация») была добавлена в .NET 4.0, но пока вы не будете обновлять до этого, вы будете застрять с циклом, или, возможно, попробуйте позвонить наTaskInterFace [], или, возможно, запрос LINQ для случая каждого элемента и создать новый список и т. Д.

a и b имеют тип IEnumerable<tblTestA> и IEnumerable<tblTestB> Хотя list.AddRange требует параметра, чтобы иметь тип типа IEnumerable<TestInterface>

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top