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

Кажется, что объект 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>

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

Ответов (14)

Решение

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

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

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

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

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

Да, из .NET 3.5 можно:

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

Мм, благодаря предыдущим комментариям я нашел два способа выяснить это. Первый - это получение строкового списка элементов и преобразование его в список объектов 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();

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

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

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

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

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

Майк - я считаю, что контравариантность также недопустима в C#.

Дополнительные сведения см. В разделе « Вариативность параметра универсального типа» в среде CLR .

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

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

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

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

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

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

Для этого до 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();

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

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

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

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

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

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

Вот еще одно решение до .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;
}

(На основе примера Зубы)

У меня есть:

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

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

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

.Cast it to the type you want to get an IEnumerable from that type, then typecast the IEnemuerable to the List<> you want.