В С#, почему объект List <string> не может быть сохранен в переменной List <object>

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

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>

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

Ответ 1

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

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

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

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

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

Ответ 2

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

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

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

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

Ответ 3

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

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

Ответ 4

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

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

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

Ответ 5

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

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

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

Ответ 9

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

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

Ответ 10

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

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

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

Ответ 11

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

Ответ 12

У меня есть:

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

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

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

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

Ответ 13

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

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

Ответ 14

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