В C# почему объект List<string> не может храниться в переменной List<string><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 ответов

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

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

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

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

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

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

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

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

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

    Вместо этого можно попробовать код 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 без проблем.

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

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

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

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

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

    Для этого предварительно 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();
    
  6. Это на самом деле так, что вы не пытаетесь поместить какой — либо странный «объект» в свой вариант списка «ol» (какList<object>, казалось бы, позволяет) — потому что ваш код будет аварийно завершать работу (потому что список действительно List<string>и будет принимать только объекты строкового типа). Поэтому вы не можете привести переменную к более общей спецификации.

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

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

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

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

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

  9. У меня есть:

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

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

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

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

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

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

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

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

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