Неправильно ли приводить перечислитель дочернего класса к перечислителю родительского класса?
-
04-07-2019 - |
Вопрос
У меня ошибка в моей сборке, которая гласит:
Ошибка 12 Не удается неявно преобразовать введите 'System.Коллекции.Общий.IEnumerator (ИЕнумератор)< Базовый класс>' в 'Систему.Коллекции.Общий.I - нумератор< IParentClass>'.Существует явное преобразование (вы пропускаете приведение?)
Разве это неправильно - просто отбросить это?
Это мой код:
public Dictionary<Int32, BaseClass> Map { get; private set; }
public IEnumerator<BaseClass> GetEnumerator()
{
return this.Map.Values.GetEnumerator();
}
public IEnumerator<IParentClass> IEnumerable<IParentClass>.GetEnumerator()
{
return this.GetEnumerator(); // ERROR!
}
Мой вопрос в том, могу ли я просто изменить эту строку:
return this.GetEnumerator();
Для:
return (IEnumerator<IParentClass>)this.GetEnumerator();
(без каких-либо серьезных побочных эффектов)?
Принятый ответ:
Я изменил функцию на следующую (после прочтения поста Джона Скита):
IEnumerator<IParentClass> IEnumerable<IParentClass>.GetEnumerator()
{
return this.Map.Values.Cast<IParentClass>().GetEnumerator();
}
Решение
Нет, вы не можете, потому что дженерики на данный момент не являются ковариантными в C #.Сам .NET имеет некоторую поддержку (для делегатов и интерфейсов), но на самом деле он еще не используется.
Если бы вы возвращались IEnumerable<BaseClass>
вместо того , чтобы IEnumerator<BaseClass>
(и предполагая .NEt 3.5) вы могли бы использовать Enumerable.Cast
- но в настоящее время вам нужно будет написать свой собственный метод расширения, например
public static IEnumerator<TParent> Upcast<TParent, TChild>
(this IEnumerator<TChild> source)
where TChild : TParent
{
while (source.MoveNext())
{
yield return source.Current;
}
}
В качестве альтернативы в вашем случае вы могли бы использовать Приведение ранее:
return this.Map.Values.Cast<BaseClass>().GetEnumerator();
Другие советы
Нет, вы не можете, по крайней мере, в C # 3.0 и ниже вариативность интерфейса не поддерживается.Смотрите отличную серию Эрика Липперта об этом, и в частности этот.
Нет, это небезопасно, смотрите ниже:
использование системы.Коллекции.Общий;класс Foo { } панель классов :Foo { } Фу
static class Program
{
static IEnumerator<Foo> GetBase() {
yield return new Foo();
yield return new Bar();
}
static IEnumerator<Bar> GetDerived()
{
return (IEnumerator<Bar>)GetBase();
}
static void Main()
{
var obj = GetDerived(); // EXCEPTION
}
}
Однако вы должны иметь возможность использовать блок итератора для выполнения приведения за вас?
static IEnumerator<Bar> GetDerived()
{
using (IEnumerator<Foo> e = GetBase())
{
while (e.MoveNext())
{
// or use "as" and only return valid data
yield return (Bar)e.Current;
}
}
}
Рассуждать почему это неуместно, картинка вместо Enumerator
, а List
.Оба используют универсальные аргументы - компилятор не обрабатывает ни один из них особым образом по отношению к универсальным аргументам.
void doStuff() {
List<IParentThing> list = getList();
list.add(new ChildThing2());
}
List<IParentThing> getList() {
return new List<ChildThing1>(); //ERROR!
}
Этот первый метод хорош - список IParentThing
ы должны быть в состоянии получить ChildThing2
.Но список из ChildThing1
s не может справиться с ChildThing2
, или действительно любой разработчик IParentThing
кроме ChildThing1
- другими словами, если List<ChildThing1>
было разрешено разыгрывать в качестве List<IParent>
, он должен был бы быть в состоянии справиться с ВСЕ подклассы IParentThing
, не просто IParentThing
и ChildThing1
.
Обратите внимание, что в Java generics есть способ сказать, что "Мне нужен список всего, что наследуется от этого" в дополнение к "Мне нужен список всего, что наследуется от этого", что позволяет создавать более интересные (и, на мой взгляд, элегантные) решения некоторых проблем.
IEnumerator<BaseClass>
и IEnumerator<ParentClass>
не связаны, несмотря на то, что их общие параметры являются.Я бы вместо этого использовал LINQ Select
метод расширения выглядит следующим образом:
return this.Select(x => (IParentClass)x).GetEnumerator();
или тот Cast
способ расширения:
return this.Cast<IParentClass>().GetEnumerator();