Реализации интерфейса и возвращаемые типы - PullRequest
4 голосов
/ 07 января 2012

Класс List<T> реализует интерфейс IEnumerable<T>. У него есть метод GetEnumerator, который возвращает List<T>.Enumerator.

У меня есть класс, как показано ниже, который выдает ошибку компиляции, говоря, что возвращаемый тип GetEnumerator не соответствует интерфейсу.

public class InsertionSortedSet<T> : IEnumerable<T>
{
    public struct Enumerator : IEnumerator<T>
    {
        // Required interface implemented
    }

    // Other interface methods implemented

    public Enumerator GetEnumerator()
    {
        return new Enumerator(this);
    }
}

'Entities.Helpers.InsertionSortedSet' не реализует элемент интерфейса 'System.Collections.Generic.IEnumerable.GetEnumerator ()'. Entities.Helpers.InsertionSortedSet.GetEnumerator () не может реализовать System.Collections.Generic.IEnumerable.GetEnumerator (), потому что у него нет соответствующего возвращаемого типа System.Collections.Generic.IEnumerator.

Учитывая, что List<T>, кажется, возвращает свой собственный класс Enumerator (не интерфейс), и все же он реализует интерфейс Enumeration<T>, я в замешательстве, так как не вижу, в чем я отличаюсь этот класс.

Что не так с моей настройкой, которая приводит к сбою, где List<T> работает?


Я хочу вернуть InsertionSortedSet<T>.Enumerator, а не интерфейс, поскольку это позволяет избежать бокса, который мне нужно вырезать.

Ответы [ 3 ]

7 голосов
/ 07 января 2012

Жалуется, потому что GetEnumerator () возвращает IEnumerator<T> для интерфейса IEnumerable<T>. Для удовлетворения ваш тип должен возвращать IEnumerator<T> (и явный также для IEnumerator).

Но во многих случаях желательно, чтобы класс возвращал более конкретный тип, чем тот, который указан в интерфейсе, но интерфейсы не допускают ковариантные типы возврата, подобные этому. Таким образом, чтобы сделать это, вы можете делать то, что List<T> делает, и GetEnumerator() возвращает ваш конкретный перечислитель, но вы также должны реализовать явные реализации для IEnumerable.GetEnumerator(), которые возвращают IEnumerator и для IEnumerable<T>.GetEnumerator() который возвращает IEnumerator<T>:

    // This is your specific version, like List<T> does
    public Enumerator GetEnumerator()
    {
        return new Enumerator(this);
    }

    // This is the one with the return value IEnumerator<T> expects
    IEnumerator<T> IEnumerable<T>.GetEnumerator()
    {
        return new Enumerator(this);
    }

    // Plus, it also expects this as well to satisfy IEnumerable
    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }

Если вы посмотрите на List<T>, вы увидите, что он реализует GetEnumerator() три раза:

  • Однажды явно для IEnumerable<T>
  • Однажды явно для Enumerable
  • Однажды с очень специфичным для списка возвратом

Вы могли бы сделать то же самое в своем классе, если хотите (что, как он и задумал, это способ, которым вы начали), но если вы делаете это, вы должны явно реализовать IEnumerable<T>.GetEnumerator() и IEnumerable.GetEnumerator()

Если вы перейдете к определению, вы можете увидеть другие определения в браузере объектов, выбрав различные интерфейсы, которым он удовлетворяет (или назначив экземпляр List<T> для ссылки IEnumerable или IEnumerable<T> и перейдя к определению ):

Screenshot of Object Explorer

0 голосов
/ 07 января 2012

То, что вы ищете, называется ковариацией возвращаемого типа, подробнее здесь Поддерживает ли C # ковариацию возвращаемого типа?

Этого можно добиться с помощью явной реализации интерфейса для возврата IEnumerator<T> и реализации открытого метода с тем же именем для возврата Enumerator.

0 голосов
/ 07 января 2012

Чтобы позволить итерации класса с использованием foreach, не нужно реализовывать какой-либо интерфейс.Все, что вам нужно, - это реализовать метод GetEnumerator, который возвращает ваш Enumerator<T>.

. Но если вы хотите, чтобы ваш класс поддерживал стандартный интерфейс итерации IEnumerable<T>, вы можете реализовать его просто: IEnumerable<T>.GetEnumerator() { return GetEnumerator(); }

...