Приведение из IEnumerable < Object > в IEnumerable < string >
-
03-07-2019 - |
Вопрос
Недавно я обнаружил очень удивительное поведение в c #.
У меня был метод, который принимает IEnumerable < Object >
в качестве параметра, и я передавал
IEnumerable < string >
, но это невозможно.
В то время как в c # все может быть выгружено в Object, почему это невозможно?
Это полностью сбивает с толку меня.
Пожалуйста, кто-нибудь прояснит мне этот вопрос.
Решение
Техническим термином для этого является то, что дженерики инвариантны в C # 3.0 и более ранних версиях. Начиная с C # 4.0, актерский состав работает.
Инвариант означает, что между двумя универсальными типами нет связи только потому, что их <тип> параметры универсального типа связаны (то есть являются подтипами или супертипами друг друга).
В вашем примере нет никакой типизации между IEnumerable < object >
и IEnumerable < string >
только потому, что string является подтипом объекта. Их просто считают двумя совершенно не связанными типами, такими как string и int (они все еще оба являются подтипами объекта, но все есть)
Для этой проблемы есть несколько обходных путей и исключений.
Во-первых, вы можете привести каждую строку отдельно к объекту, если вы используете .NET 3.0, вы можете сделать это, используя метод расширения Cast < T > ()
. В противном случае вы можете использовать foreach и поместить результат в новую переменную нужного вам статического типа.
Во-вторых, массивы являются исключением для ссылочного типа, то есть передача типа string [] в метод, принимающий object [], должна работать.
Другие советы
Как уже отмечали другие, универсальные типы являются инвариантными. IEnumerable < T >
может быть ко-вариантом, но C # в настоящее время не поддерживает указание вариантов. Ожидается, что C # 4.0 будет поддерживать варианты, так что это может быть поддержано в будущем.
Чтобы обойти это сейчас, вы можете использовать метод расширения LINQ Cast < object > ()
. Предполагая, что у вас есть метод с именем Foo
, который принимает IEnumerable < object > >
. Вы можете назвать это так,
Foo(stringEnumerable.Cast<object>());
Самый простой способ передать IEnumerable < string >
в функцию, требующую IEnumerable < object >
, заключается в преобразовании такой функции:
public IEnumerable<object> convert<T>(IEnumerable<T> enumerable)
{
foreach (T o in enumerable)
yield return o;
}
Когда выйдет C # 4, это не будет необходимым, потому что оно будет поддерживать ковариацию и контравариантность.
Вы должны использовать
IEnumerable<T>
если вы хотите передать разные типы, то вы можете запросить T, чтобы узнать, какой это тип.
Хороший способ подумать об этом - спросить себя: «Что бы произошло, если бы ты мог это сделать?». Возьмите следующий пример:
IEnumerable<String> strings=...;
IEnumerable<Object> objects = strings; // assume this were legal
objects.Add(new Integer(5)); // what the...
Мы только что добавили целое число в список строк. Компилятор делает это, чтобы сохранить безопасность типов.