Как правильно сделать интерфейс поддерживающим итерацию?
Пользователи, работающие с Delphi XE, сталкиваются с проблемой, когда пытаются реализовать интерфейс IEnumerable<T> для своих собственных классов. Это позволяет использовать объекты в циклах for и for in, что является удобным способом итерации по коллекциям. Однако, при реализации IEnumerable<T> необходимо обеспечить наличие метода GetEnumerator, который возвращает объект IEnumerator<T>. Это может вызвать затруднения, так как IEnumerable<T> наследует IEnumerable, который также требует реализации метода GetEnumerator, но без параметров типа.
Оригинальный код
Вот пример кода, который демонстрирует проблему:
unit FungibleTrollUnit;
...
type
IFungibleTroll = interface
['{03536137-E3F7-4F9B-B1F5-2C8010A4D019}']
function GetTrollName: String;
function GetTrollRetailPrice: Double;
end;
TFungibleTrolls = class(TInterfacedObject, IEnumerable<TFungibleTroll>)
protected
FTrolls: TList<TFungibleTroll>;
public
// IEnumerable
function GetEnumerator: IEnumerator<TFungibleTroll>;
// ...
end;
implementation
...
function TFungibleTrolls.GetEnumerator: IEnumerator<TFungibleTroll>;
begin
Result := FTrolls.GetEnumerator;
end;
Проблема
При попытке компиляции данного кода возникают ошибки, указывающие на отсутствие реализации метода GetEnumerator для интерфейса IEnumerable и на дублирование метода GetEnumerator с одинаковыми параметрами.
Решение
Чтобы решить эту проблему, необходимо реализовать два метода GetEnumerator: один для IEnumerable<T> и один для IEnumerable. При этом, если класс не является обобщенным, он не может напрямую "отобразить" свои требования на IEnumerable. Одним из решений является использование обобщенного списка TList<T>, что позволяет классу "заполнить" требования интерфейса IEnumerable.
Подтвержденный ответ
Для реализации итерации по интерфейсу в Delphi XE можно использовать следующий подход:
Создать класс, который реализует интерфейс IList<T>, включающий методы для получения перечислителя и добавления элементов.
Реализовать класс TBjmInterfacedList<T>, который будет хранить список элементов и предоставлять необходимые методы интерфейса IList<T>.
Использовать перечисление из TList<T>, чтобы получить итератор для списка.
Пример реализации класса TBjmInterfacedList<T>:
type
TBjmInterfacedList<T> = class(TBjmInterfacedObject, IList<T>)
strict private
FList: TList<T>;
function GetEnumerator: TList<T>.TEnumerator;
strict protected
function Add(const Value: T): Integer;
public
constructor Create; override;
destructor Destroy; override;
end;
...
implementation
...
function TBjmInterfacedList<T>.GetEnumerator: TList<T>.TEnumerator;
begin
Result := FList.GetEnumerator;
end;
Использование данного класса позволит вам итерировать элементы в цикле for:
var
for Site in Sites do begin
// ...
end;
Альтернативный ответ
Для реализации IEnumerable<T> можно использовать следующий подход:
Создать перечислитель TGenericEnumerator<T>, который реализует интерфейсы IEnumerator и IEnumerator<T>.
Создать базовый класс TNonGenericEnumerable, который реализует интерфейс IEnumerable и предоставляет абстрактный метод GetNonGenericEnumerator.
Создать класс TGenericEnumerable<T>, который наследует TNonGenericEnumerable и реализует IEnumerable<T>.
Пример реализации TGenericEnumerable<T>:
type
TGenericEnumerable<T> = class(TNonGenericEnumerable, IEnumerable<T>)
private
FList: TList<T>;
public
constructor Create;
destructor Destroy; override;
function GetNonGenericEnumerator: IEnumerator; override;
function GetEnumerator: IEnumerator<T>;
property List: TList<T> read FList;
end;
...
implementation
...
function TGenericEnumerable<T>.GetEnumerator: IEnumerator<T>;
begin
Result := TGenericEnumerator<T>.Create(FList);
end;
Использование этих классов позволит вам создать объект, поддерживающий итерацию по интерфейсу IEnumerable<T>.
Заключение
Пользователи, сталкивающиеся с проблемой реализации IEnumerable<T> в Delphi XE, могут использовать примеры кода, представленные выше, для создания итеративных интерфейсов. Важно помнить о необходимости реализации двух методов GetEnumerator для поддержки обоих интерфейсов, а также о преимуществах использования обобщенных типов для достижения большей гибкости и функциональности в вашем коде.
Эта статья представляет собой руководство по решению типичных проблем, связанных с итеративными интерфейсами в Delphi XE, и может служить полезным ресурсом для разработчиков, сталкивающихся с подобными задачами.
Пользователи Delphi XE сталкиваются с необходимостью правильно реализовать интерфейс `IEnumerable` для поддержки итерации по коллекциям, что требует реализации методов `GetEnumerator` для работы с циклами `for` и `for in`.
Комментарии и вопросы
Получайте свежие новости и обновления по Object Pascal, Delphi и Lazarus прямо в свой смартфон. Подпишитесь на наш Telegram-канал delphi_kansoftware и будьте в курсе последних тенденций в разработке под Linux, Windows, Android и iOS
Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.