Реализации интерфейса и возвращаемые типы

Класс 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, а не интерфейс, поскольку он позволяет избежать упаковки, которую мне нужно вырезать.


person George Duckett    schedule 06.01.2012    source источник


Ответы (3)


Он жалуется, потому что 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> и перейдя к определению):

Снимок экрана обозревателя объектов

person James Michael Hare    schedule 06.01.2012
comment
@GeogeDuckett: я обновил на основе этого. List‹T› реализует GetEnumerable() три раза, один раз с собственным перечислителем, специфичным для списка, и явным образом для IEnumerable и IEnumerable<T> - person James Michael Hare; 07.01.2012
comment
Аааа, я вижу. Что меня обмануло, так это то, что щелчок правой кнопкой мыши на List<T> и выбор Go to Definition не показывает его. Где вы побывали, чтобы увидеть все GetEnumerator методы? - person George Duckett; 07.01.2012
comment
@GeorgeDuckett: я использовал декомпилятор для просмотра сборок .NET Framework, хотя вы также можете получить исходный код .NET в Интернете. - person James Michael Hare; 07.01.2012
comment
Спасибо за дополнение к вашему ответу. Я совсем забыл о браузере объектов. - person George Duckett; 07.01.2012
comment
@GeorgeDuckett: Не беспокойтесь :-) Я поражен дома, в VS есть много классных вещей, о которых я иногда забываю... - person James Michael Hare; 07.01.2012

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

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

person STO    schedule 06.01.2012
comment
Это помогает в моем случае использования, однако остается вопрос; Как List<T> реализует интерфейс, когда, насколько я вижу, он делает то, что я пытаюсь сделать (возвращая конкретный тип, а не интерфейс). - person George Duckett; 07.01.2012
comment
открытый класс List‹T›: IEnumerable‹T› { public struct Enumerator: IEnumerator‹T› { ... } public Enumerator GetEnumerator() { return ... } IEnumearator‹T› IEnumerable‹T›.GetEnumerator() { return ПолучитьПеречислитель(); } } - person STO; 07.01.2012

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

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

person Euphoric    schedule 06.01.2012