Неопределенность дженериков [дубликат]

В моем случае я использовал mb_split, который использует регулярное выражение. Поэтому мне также пришлось вручную убедиться, что кодировка регулярного выражения была utf-8, выполнив mb_regex_encoding('UTF-8');

. В качестве побочной заметки я также обнаружил, запустив mb_internal_encoding(), что внутренняя кодировка не была utf-8 , и я изменил это, выполнив mb_internal_encoding("UTF-8");.

28
задан Sam I am 6 June 2013 в 17:08
поделиться

6 ответов

Это потому, что List<T> является in-variant, а не co-variant, поэтому вы должны перейти на IEnumerable<T>, который поддерживает co-variant, он должен работать:

IEnumerable<BaseClass> bcl = new List<DerivedClass>();
public void doSomething(IEnumerable<BaseClass> bc)
{
    // do something with bc
}

Информация о в общем случае

38
ответ дан Cuong Le 25 August 2018 в 04:02
поделиться

Объяснения, которые я нашел, просто сказали, что это как-то нарушает безопасность типа, но я этого не вижу. В чем заключается риск компилятора, позволяющего преобразовать из List<DerivedClass> в List<BaseClass>?

Этот вопрос задается почти каждый день.

A List<Mammal> не может быть преобразуется в List<Animal>, потому что вы можете помещать ящерицу в список животных . List<Mammal> не может быть преобразован в List<Giraffe>, потому что может быть тигр в списке уже .

Поэтому List<T> должен быть инвариантным в T.

Однако List<Mammal> можно преобразовать в IEnumerable<Animal> (как из C # 4.0), потому что на IEnumerable<Animal> нет метода, который добавляет ящерицу. IEnumerable<T> является ковариантным в T.

40
ответ дан Eric Lippert 25 August 2018 в 04:02
поделиться

Решением, которое я использовал, было создание класса расширения:

public static class myExtensionClass 
{
    public void doSomething<T>(List<BaseClass> bc) where T: BaseClass
    {
        // do something with bc
    }
}

Он использует общий, но когда вы его вызываете, вам не нужно выделять класс, поскольку вы уже «сказали» компилятор тип тот же самый из расширенного класса.

Вы бы назвали его следующим образом:

List<DerivedClass> lst = new List<DerivedClass>();
lst.doSomething();
0
ответ дан Marlon 25 August 2018 в 04:02
поделиться

Когда у вас есть класс, полученный из базового класса, любые контейнеры этих классов автоматически не выводятся. Таким образом, вы не можете просто отличить List<Derived> от List<Base>.

Используйте .Cast<T>() для создания нового списка, в который каждый объект возвращается в базовый класс:

List<MyDerived> list1 = new List<MyDerived>();
List<MyBase> list2 = list1.Cast<MyBase>().ToList();

Обратите внимание, что это новый список, а не литая версия исходного списка, поэтому операции над этим новым списком не будут отображаться в исходном списке. Однако операции над содержащимися объектами будут отражаться.

2
ответ дан Matt Houser 25 August 2018 в 04:02
поделиться

Поведение, которое вы описываете, называется covariance & ndash; если A является B, то List<A> является List<B>.

Однако для изменяемых типов, таких как List<T>, это в основном небезопасно.

Если бы это было возможно, метод мог бы добавить new OtherDerivedClass() в список, который может фактически удерживать DerivedClass.

Ковариация безопасна для неизменные типы, хотя .Net поддерживает только его в интерфейсах и делегатах. Если вы измените параметр List<T> на IEnumerable<T>, это будет работать

12
ответ дан SLaks 25 August 2018 в 04:02
поделиться

Если вы могли бы написать

List<BaseClass> bcl = new List<DerivedClass>(); 

, вы могли бы вызвать

var instance = new AnotherClassInheritingFromBaseClass();
bc1.Add(instance);

Добавление экземпляра, который не является DerivedClass в список.

1
ответ дан vc 74 25 August 2018 в 04:02
поделиться
Другие вопросы по тегам:

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