В моем случае я использовал mb_split
, который использует регулярное выражение. Поэтому мне также пришлось вручную убедиться, что кодировка регулярного выражения была utf-8, выполнив mb_regex_encoding('UTF-8');
. В качестве побочной заметки я также обнаружил, запустив mb_internal_encoding()
, что внутренняя кодировка не была utf-8 , и я изменил это, выполнив mb_internal_encoding("UTF-8");
.
Это потому, что 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
}
Информация о в общем случае
Объяснения, которые я нашел, просто сказали, что это как-то нарушает безопасность типа, но я этого не вижу. В чем заключается риск компилятора, позволяющего преобразовать из
blockquote>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.
Решением, которое я использовал, было создание класса расширения:
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();
Когда у вас есть класс, полученный из базового класса, любые контейнеры этих классов автоматически не выводятся. Таким образом, вы не можете просто отличить List<Derived>
от List<Base>
.
Используйте .Cast<T>()
для создания нового списка, в который каждый объект возвращается в базовый класс:
List<MyDerived> list1 = new List<MyDerived>();
List<MyBase> list2 = list1.Cast<MyBase>().ToList();
Обратите внимание, что это новый список, а не литая версия исходного списка, поэтому операции над этим новым списком не будут отображаться в исходном списке. Однако операции над содержащимися объектами будут отражаться.
Поведение, которое вы описываете, называется covariance & ndash; если A
является B
, то List<A>
является List<B>
.
Однако для изменяемых типов, таких как List<T>
, это в основном небезопасно.
Если бы это было возможно, метод мог бы добавить new OtherDerivedClass()
в список, который может фактически удерживать DerivedClass
.
Ковариация безопасна для неизменные типы, хотя .Net поддерживает только его в интерфейсах и делегатах. Если вы измените параметр List<T>
на IEnumerable<T>
, это будет работать
Если вы могли бы написать
List<BaseClass> bcl = new List<DerivedClass>();
, вы могли бы вызвать
var instance = new AnotherClassInheritingFromBaseClass();
bc1.Add(instance);
Добавление экземпляра, который не является DerivedClass в список.