Почему я не могу разыграть из списка <MyClass>в список <object>? - PullRequest
23 голосов
/ 04 мая 2011

У меня есть список объектов моего типа QuoteHeader, и я хочу передать этот список в виде списка объектов методу, который может принять List<object>.

Myстрока кода читает ...

Tools.MyMethod((List<object>)MyListOfQuoteHeaders);

Но я получаю следующую ошибку во время разработки ...

Cannot convert type 'System.Collections.Generic.List<MyNameSpace.QuoteHeader>' 
to 'System.Collections.Generic.List<object>'

Нужно ли что-то делать с моим классом, чтобы это разрешить?Я думал, что все классы наследуются от объекта, поэтому я не могу понять, почему это не сработает?

Ответы [ 6 ]

41 голосов
/ 04 мая 2011

Причина, по которой это не законно, заключается в том, что это небезопасно. Предположим, это было законно:

List<Giraffe> giraffes = new List<Giraffe>();
List<Animal> animals = giraffes; // this is not legal; suppose it were.
animals.Add(new Tiger());  // it is always legal to put a tiger in a list of animals

Но "животные" - это на самом деле список жирафов; Вы не можете поместить тигра в список жирафов.

В C # это, к сожалению, допустимо для массивов ссылочного типа:

Giraffe[] giraffes = new Giraffe[10];
Animal[] animals = giraffes; // legal! But dangerous because...
animals[0] = new Tiger(); // ...this fails at runtime!

В C # 4 это допустимо для IEnumerable , но не IList :

List<Giraffe> giraffes = new List<Giraffe>();
IEnumerable<Animal> animals = giraffes; // Legal in C# 4
foreach(Animal animal in animals) { } // Every giraffe is an animal, so this is safe

Это безопасно, потому что IEnumerable<T> не предоставляет никаких методов, которые принимает a T.

Для решения вашей проблемы вы можете:

  • Создать новый список объектов из старого списка.
  • Заставьте метод взять объект [], а не List<object>, и использовать ковариацию небезопасных массивов.
  • Сделайте метод универсальным, чтобы он занял List<T>
  • Заставить метод принимать IEnumerable
  • Заставьте метод взять IEnumerable<object> и использовать C # 4.
5 голосов
/ 04 мая 2011

Вы не можете привести List<OneType> к List<OtherType>, так как на самом деле это экземпляры списка, который вы хотите разыграть, а также сам список.

существует метод расширения, который позволит вам сделать это ( ссылка MSDN ):

IEnumerable<Object> myNewEnumerable = myEnumerable.Cast<Object>();

Этот метод будет пытаться привести каждый экземпляр спискаодин тип к другому типу и добавить их в новый перечисляемый.оно выдаст исключение, если какой-либо экземпляр не может быть приведен.

Что касается системы, то два типа для ваших списков - это просто разные типы, так что все равно, что сказать:

A objectOfTypeA;
B objectOfTypeB = (B) objectofTypeA;

Чтобы иметь возможность выполнять приведение, должно быть неявное или явное преобразование между доступными типами, которого нет (если вы не предоставили один, что вы могли бы сделать).

вы ожидаете, что это сработает, потому что List<object> всегда сможет содержать любой тип в другом списке, но если вы подумаете об этом в этих терминах, вы поймете, почему это не так.

Я уверен, что есть более технически грамотный ответ, но я думаю, что в этом суть.

Вам может быть интересно прочитать серию Эрика Липперта о Ковариантности и Контравариантности , как этоможет быть полезным для вас.

Этот вопрос также может быть полезным

3 голосов
/ 10 ноября 2012
List<MyClass> x = someList.Select(f => (MyClass)f).ToList();
1 голос
/ 04 мая 2011

Я предполагаю, что вы имеете в виду, что списки относятся к типам, которые наследуются друг от друга или могут быть преобразованы из одного типа в другой - в этом случае попробуйте следующее:

Tools.MyMethod(MyListOfQuoteHeaders.Cast<OtherType>());
0 голосов
/ 05 мая 2011

Спасибо за многочисленные ответы.

Я объясню, что я хотел сделать, и что я нашел в качестве решения.

Мне нужен был метод, который я мог бы вызвать, передав список объектов любого типа, а затем вывел этот список в XML. В метод также будет передана строка, которая будет местоположением пути структуры системного файла, указывающим на местоположение, в которое будет сохранен файл XML. Поскольку у меня постоянно растет число классов и типов, я хотел избежать написания нескольких методов для обслуживания каждого типа классов. Я не уверен, что я даже пошел по этому пути правильно, но это облегченное решение моей проблемы и работает. Если у вас есть какие-либо проблемы или комментарии, пожалуйста, не стесняйтесь ...

Итак ... мой метод теперь выглядит так ...

    public static Enums.Status WriteListToXML<T>(string outputPath, List<T> inboundList)
    {
        try
        {
            XmlSerializer xmlSerializer = new XmlSerializer(inboundList.GetType());
            using (StreamWriter streamWriter = System.IO.File.CreateText(outputPath))
            {
                xmlSerializer.Serialize(streamWriter, inboundList);
            }
            return Enums.Status.Success;
        }
        catch (Exception ex)
        {
            return Enums.Status.Failure;
        }
    }

... и моя вызывающая линия читает ...

WriteListToXML<QuoteHeader>(@"C:\XMLDocuments\output\QuoteHeaders.xml", quoteHeadersList);

Как я уже сказал, это может быть не самое аккуратное решение, но оно хорошо работает в моем сценарии.

0 голосов
/ 04 мая 2011

Проблема в том, что во время компиляции компилятор испускает 2 отдельных класса: 1, представляющий List<MyClass>, и один, представляющий List<Object>. По сути, это 2 отдельных типа. Так работают универсальные типы, по крайней мере, в .Net.

Предполагая, что это .Net, вы можете сделать

MyListOfQuoteHeaders.Cast<Object>()

что в основном делает

var objects = new List<Object>();

foreach(var item in MyListOfQuoteHeaders)
{
   objects.Add((object)item);
}

return objects;
...