Почему в C # нельзя хранить объект List в переменной List

Кажется, что объект List не может быть сохранен в переменной List в C # и даже не может быть явно приведен таким образом.

List<string> sl = new List<string>();
List<object> ol;
ol = sl;

приводит к невозможности неявного преобразования типа System.Collections.Generic.List<string>вSystem.Collections.Generic.List<object>

А потом...

List<string> sl = new List<string>();
List<object> ol;
ol = (List<object>)sl;

Результаты не могут преобразовать тип System.Collections.Generic.List<string>вSystem.Collections.Generic.List<object>

Конечно, вы можете сделать это, вытащив все из списка строк и поместив его обратно по одному, но это довольно запутанное решение.

9.08.2008 02:32:01
Это изменится с C # 4.0, так что вы можете захотеть искать ковариацию и контравариантность. Это позволит такие вещи безопасным способом.
John Leidegren 4.03.2009 08:10:42
Более или менее дубликаты: stackoverflow.com/questions/317335/…
Joel in Gö 20.05.2009 12:25:12
John> C # 4 не допустит этого. Подумайте о ol.Add (новый объект ());
Guillaume 22.03.2010 09:07:41
На это отвечает Джон Скит: stackoverflow.com/a/2033921/1070906
JCH2k 22.07.2015 12:15:19
14 ОТВЕТОВ
РЕШЕНИЕ

Подумайте об этом так, если вы выполняете такое приведение, а затем добавляете объект типа Foo в список, список строк больше не является согласованным. Если бы вы перебрали первую ссылку, вы бы получили исключение приведения класса, потому что, как только вы нажмете экземпляр Foo, Foo не сможет быть преобразован в строку!

Как примечание стороны, я думаю, что было бы более важно, можете ли вы сделать обратное приведение:

List<object> ol = new List<object>();
List<string> sl;
sl = (List<string>)ol;

Я не использовал C # некоторое время, поэтому я не знаю, является ли это законным, но этот тип приведения действительно (потенциально) полезен. В этом случае вы переходите от более общего класса (объекта) к более конкретному классу (строке), который расширяется от общего. Таким образом, если вы добавляете в список строк, вы не нарушаете список объектов.

Кто-нибудь знает или может проверить, является ли такое приведение законным в C #?

38
9.08.2008 02:50:14
Я думаю, что ваш пример страдает от той же проблемы. Что произойдет, если в ol есть что-то, что не является строкой? Проблема в том, что некоторые методы в List будут работать нормально, например, добавление / вставка. Но итерации могут быть реальной проблемой.
Chris Ammerman 30.09.2008 16:40:51
У Эрика Липперта есть отличная серия постов в блоге на эту тему: почему это может сработать, добавив ковариационные и контравариантные противоречия к общим методам, но может никогда не сработать так, как хотелось бы на уровне класса. is.gd/3kQc
Chris Ammerman 30.09.2008 16:44:20
@ChrisAmmerman: Если olбы в нем было что-то, что не является строкой, я полагаю, что ожидал бы сбой приведения во время выполнения. Но если вы действительно столкнетесь с неприятностями, это произойдет, если приведение преуспеет, и тогда к нему будет добавлено olчто-то, что не является строкой. Поскольку slссылается на один и тот же объект, теперь ваш List<string>будет содержать не строку. Это Addпроблема, которая, я думаю, оправдывает, почему этот код не будет компилироваться, но он скомпилируется, если вы измените List<object> olна IEnumerable<object> ol, у которого нет Add. (Я проверил это в C # 4.)
Tim Goodman 2.08.2013 17:44:00
С этим изменением он компилируется, но выдает, InvalidCastExceptionпотому что тип времени выполнения по- olпрежнему List<object>.
Tim Goodman 2.08.2013 17:45:20

Причина в том, что обобщенный класс like List<>в большинстве случаев рассматривается как обычный класс. например, когда вы говорите, List<string>()что компилятор говорит ListString()(который содержит строки). [Технический фолк: это очень простая и понятная версия того, что происходит]

Следовательно, очевидно, что компилятор не может быть достаточно умным, чтобы преобразовать ListString в ListObject путем приведения элементов его внутренней коллекции.

Вот почему есть методы расширения для IEnumerable, такие как Convert (), которые позволяют легко предоставлять преобразование для элементов, хранящихся в коллекции, что может быть так же просто, как приведение к одному.

11
9.08.2008 02:38:02

Майк - я считаю, что контравариантность не разрешена в C #

Посмотрите Универсальный параметр параметра дисперсии в CLR для получения дополнительной информации.

3
27.01.2013 13:29:04

Это во многом связано с ковариацией, например, универсальные типы рассматриваются как параметры, и если параметры не разрешаются должным образом для более конкретного типа, то операция завершается неудачей. Смысл этого в том, что вы действительно не можете привести к более общему типу, подобному объекту. И, как утверждает Рекс, объект List не будет преобразовывать каждый объект для вас.

Вы можете попробовать вместо этого код FF:

List<string> sl = new List<string>();
//populate sl
List<object> ol = new List<object>(sl);

или:

List<object> ol = new List<object>();
ol.AddRange(sl);

ol (теоретически) скопирует все содержимое sl без проблем.

6
9.08.2008 02:58:26

Если вы используете .NET 3.5, взгляните на метод Enumerable.Cast. Это метод расширения, поэтому вы можете вызывать его прямо в списке.

List<string> sl = new List<string>();
IEnumerable<object> ol;
ol = sl.Cast<object>();

Это не совсем то, что вы просили, но должны сделать свое дело.

Редактировать: Как отметил Zooba, вы можете вызвать ol.ToList (), чтобы получить список

36
10.08.2008 20:54:56

Вы не можете приводить между универсальными типами с разными параметрами типа. Специализированные универсальные типы не являются частью одного и того же дерева наследования и, следовательно, являются несвязанными типами.

Для этого предварительно NET 3.5:

List<string> sl = new List<string>();
// Add strings to sl

List<object> ol = new List<object>();

foreach(string s in sl)
{
    ol.Add((object)s);  // The cast is performed implicitly even if omitted
}

Используя Linq:

var sl = new List<string>();
// Add strings to sl

var ol = new List<object>(sl.Cast<object>());

// OR
var ol = sl.Cast<object>().ToList();

// OR (note that the cast to object here is required)
var ol = sl.Select(s => (object)s).ToList();
14
14.08.2008 04:34:34

Это на самом деле так, что вы не пытаетесь поместить какой-либо нечетный «объект» в ваш вариант списка «ол» (как это List<object>может показаться разрешающим) - потому что тогда ваш код потерпит крах (потому что список действительно есть List<string>и будет принимать только объекты типа String ). Вот почему вы не можете привести свою переменную к более общей спецификации.

В Java все наоборот, у вас нет универсальных шаблонов, и вместо этого все является списком объектов во время выполнения, и вы действительно можете вставить любой странный объект в свой предположительно строго типизированный список. Ищите «Reified generics», чтобы увидеть более широкое обсуждение проблемы Java ...

1
10.08.2008 14:35:48

Такая ковариация в дженериках не поддерживается, но вы можете сделать это с массивами:

object[] a = new string[] {"spam", "eggs"};

C # выполняет проверки во время выполнения, чтобы помешать вам, скажем, поместить intв a.

1
9.10.2008 21:54:25

Да, вы можете из .NET 3.5:

List<string> sl = new List<string>();
List<object> ol = sl.Cast<object>().ToList();
5
18.10.2008 20:26:02

Я думаю, что это (противоположность) будет фактически поддерживаться в C # 4.0. http://blogs.msdn.com/charlie/archive/2008/10/27/linq-farm-covariance-and-contravariance-in-visual-studio-2010.aspx

2
7.11.2008 19:01:21

Вот еще одно решение до .NET 3.5 для любого IList, содержимое которого может быть явным образом приведено.

public IList<B> ConvertIList<D, B>(IList<D> list) where D : B
{
    List<B> newList = new List<B>();

    foreach (D item in list)
    {
        newList.Add(item);
    }

    return newList;
}

(На примере Zooba)

0
8.06.2009 16:34:54

У меня есть:

private List<Leerling> Leerlingen = new List<Leerling>();

И я собирался заполнить его данными, собранными в том, List<object> что, наконец, сработало для меня, было вот этим:

Leerlingen = (List<Leerling>)_DeserialiseerLeerlingen._TeSerialiserenObjecten.Cast<Leerling>();

.Castэто типа вы хотите получить IEnumerableот этого типа, то напечатанный IEnemuerableк List<>вы хотите.

0
9.11.2011 18:06:07

Мм, благодаря предыдущим комментариям я нашел два способа это выяснить. Первый - получение списка строк элементов и приведение его к списку объектов IEnumerable:

IEnumerable<object> ob;
List<string> st = new List<string>();
ob = st.Cast<object>();

И второй - избегать типа объекта IEnumerable, просто приводя строку к типу объекта, а затем используя функцию «toList ()» в том же предложении:

List<string> st = new List<string>();
List<object> ob = st.Cast<object>().ToList();

Мне больше нравится второй способ. Надеюсь, это поможет.

0
17.03.2016 21:38:26
List<string> sl = new List<string>();
List<object> ol;
ol = new List<object>(sl);
0
27.04.2017 09:52:30