Неправильно ли приводить перечислитель дочернего класса к перечислителю родительского класса?

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

  •  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.Но список из ChildThing1s не может справиться с ChildThing2, или действительно любой разработчик IParentThing кроме ChildThing1 - другими словами, если List&lt;ChildThing1> было разрешено разыгрывать в качестве List&lt;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();
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top