Логика и ее применение к Collections.Generic и наследование

StackOverflow https://stackoverflow.com/questions/110121

  •  02-07-2019
  •  | 
  •  

Вопрос

Все наследуется от объекта.Это основа наследования.Все можно неявно привести к дереву наследования, т.е.

object me = new Person();

Следовательно, доведя это до логического завершения, группа Людей также будет группой объектов:

List<Person> people = new List<Person>();
people.Add(me);
people.Add(you);
List<object> things = people; // Ooops.

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

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

Для справки: я считаю, что ситуация, в которой я хотел такого поведения, была универсальной функцией печати, которая отображала списки объектов путем вызова ToString() и красиво форматировала строки.

Это было полезно?

Решение

Хорошо, каждый, кто использовал дженерики в .net, наверняка сталкивался с этим в тот или иной момент.

Да, интуитивно должно работать.Нет, в текущей версии компилятора C# это не так.

У Эрика Липперта есть действительно хорошее объяснение этой проблемы (оно состоит из одиннадцати частей или около того и местами сбивает вас с толку, но его стоит прочитать).Видеть здесь.

редактировать:

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

Другие советы

вы можете использовать linq для его приведения:

IEnumerable<Person> oldList = someIenumarable;
IEnumerable<object> newList = oldlist.Cast<object>()

На первый взгляд, это не имеет интуитивного смысла.Но это так.Посмотрите на этот код:

List<Person> people = new List<Person>();
List<object> things = people; // this is not allowed
// ...
Mouse gerald = new Mouse();
things.add(gerald);

Теперь у нас внезапно есть List из Person объекты...с Mouse внутри него!

Это объясняет, почему присвоение объекта типа A<T> к переменной типа A<S> не допускается, даже если S является супертипом T.

Обходной путь linq является хорошим.Другой обходной путь, поскольку вы используете объект типа, — передать список как IEnumerable (а не общую версию).

Редактировать:C# 4 (в настоящее время бета-версия) поддерживает параметр ковариантного типа в IEnumerable.Хотя вы не сможете напрямую назначать List<object>, вы можете передать свой список методу, ожидающему IEnumerable<object>.

Хотя то, что вы пытаетесь сделать, действительно логично, на самом деле это функция, которую многие языки изначально не поддерживают.Это так называемая ко/контра-вариация, которая связана с тем, когда и как компилятор может неявно переводить объекты из одного объекта в другой.К счастью, C# 4.0 принесет на арену C# ковариацию и контравариантность, и подобные неявные приведения станут возможными.

Для подробного объяснения этого может быть полезно следующее видео Channel9:

http://channel9.msdn.com/shows/Going+Deep/Inside-C-40-dynamic-type-optional-parameters-more-COM-Friendly/

С помощью методов расширения linq вы можете сделать

IEnumerable<object> things = people.Cast<object>();
List<object> things = people.Cast<object>().ToList();

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top