Если вы действительно хотите создать класс, который реализует IEnumerable , вы можете сделать это так:
unit uGenericEnumerable;
interface
uses SysUtils, Classes, Generics.Collections;
type TGenericEnumerator<T> = class(TInterfacedObject, IEnumerator, IEnumerator<T>)
private
FList: TList<T>;
FIndex: Integer;
protected
function GenericGetCurrent: T;
public
constructor Create(AList: TList<T>);
procedure Reset;
function MoveNext: Boolean;
function GetCurrent: TObject;
function IEnumerator<T>.GetCurrent = GenericGetCurrent;
property Current: T read GenericGetCurrent;
end;
type TNonGenericEnumerable = class(TInterfacedObject, IEnumerable)
protected
function GetNonGenericEnumerator: IEnumerator; virtual; abstract;
public
function IEnumerable.GetEnumerator = GetNonGenericEnumerator;
end;
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
{ TGenericEnumerator<T> }
constructor TGenericEnumerator<T>.Create(AList: TList<T>);
begin
inherited Create;
FList := AList;
FIndex := -1;
end;
procedure TGenericEnumerator<T>.Reset;
begin
FIndex := -1;
end;
function TGenericEnumerator<T>.MoveNext: Boolean;
begin
if FIndex < FList.Count then
begin
Inc(FIndex);
Result := FIndex < FList.Count;
end
else
begin
Result := False;
end;
end;
function TGenericEnumerator<T>.GenericGetCurrent: T;
begin
Result := FList[FIndex];
end;
function TGenericEnumerator<T>.GetCurrent: TObject;
begin
// If T has not been constrained to being a class, raise an exception instead of trying to return an object.
raise Exception.Create('Cannot use this as a non-generic enumerator');
// If T has been constrained to being a class, return GenericGetCurrent.
// Result := GenericGetCurrent;
end;
{ TGenericEnumerable<T> }
constructor TGenericEnumerable<T>.Create;
begin
inherited Create;
FList := TList<T>.Create;
end;
destructor TGenericEnumerable<T>.Destroy;
begin
FList.Free;
end;
function TGenericEnumerable<T>.GetEnumerator: IEnumerator<T>;
begin
Result := TGenericEnumerator<T>.Create(FList);
end;
function TGenericEnumerable<T>.GetNonGenericEnumerator: IEnumerator;
begin
Result := GetEnumerator;
end;
end.
Теперь ваш FungibleTrollUnit будет выглядеть примерно так:
unit FungibleTrollUnit;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics,
Controls, Forms, Generics.Collections,
uGenericEnumerable;
type
IFungibleTroll = interface
['{03536137-E3F7-4F9B-B1F5-2C8010A4D019}']
function GetTrollName:String;
function GetTrollRetailPrice:Double;
end;
IFungibleTrolls = interface(IEnumerable<IFungibleTroll>)
['{090B45FB-2925-4BFC-AE97-5D3F54E1C575}']
function GetTrolls: TList<IFungibleTroll>;
function FindSingleItemByName(aName:String):IFungibleTroll;
function FindMultipleItemsByName(aName:String):IEnumerable<IFungibleTroll>;
property Trolls:TList<IFungibleTroll> read GetTrolls;
end;
TFungibleTrolls = class (TGenericEnumerable<IFungibleTroll>, IFungibleTrolls, IEnumerable<IFungibleTroll>)
public
function GetTrolls: TList<IFungibleTroll>;
function FindSingleItemByName(aName: String): IFungibleTroll;
function FindMultipleItemsByName(aName: String): IEnumerable<IFungibleTroll>;
property Trolls:TList<IFungibleTroll> read GetTrolls;
private
end;
implementation
uses StrUtils;
{ TFungibleTrolls }
function TFungibleTrolls.GetTrolls: TList<IFungibleTroll>;
begin
Result := List;
end;
function TFungibleTrolls.FindMultipleItemsByName(aName: String): IEnumerable<IFungibleTroll>;
var FilteredTrolls: TGenericEnumerable<IFungibleTroll>;
var Troll: IFungibleTroll;
begin
FilteredTrolls := TGenericEnumerable<IFungibleTroll>.Create;
for Troll in List do
begin
if Troll.GetTrollName = aName then
FilteredTrolls.List.Add(Troll);
end;
Result := IEnumerable<IFungibleTroll>(FilteredTrolls);
end;
function TFungibleTrolls.FindSingleItemByName(aName: String): IFungibleTroll;
var Troll: IFungibleTroll;
begin
Result := nil;
for Troll in List do
begin
if Troll.GetTrollName = aName then
Result := Troll;
break;
end;
end;
end.
Обратите внимание, что реализация IEnumerable не работает, но IEnumerable работает.
Это происходит потому, что, если T не ограничен, вы не можете преобразовать T в объект TObject.
Если T является строкойили целое число, например, тогда IEnumerator не имеет TObject для возврата.
Если T ограничено быть классом, вы можете заставить работать реализацию IEnumerable.
Если вы ограничите T быть IInterface, вы можете получить IEnumerable для работы (Delphi 2010 и после (приведениеGenericGetCurrent для IInterface, затем для TObject)), но я сомневаюсь, что это преимущество.
Я бы предпочел использовать его без ограничений и обходиться без возможности повторять все как объекты TObject.
TGenericEnumerable .GetEnumerator не может использовать FList.GetEnumerator, поскольку TList .GetEnumerator не возвращает IEnumerator
Даже если вы можете реализовать TGenericEnumerable без определения TNonGenericEnumerable, например:
type TGenericEnumerable<T> = class(TInterfacedObject, IEnumerable, IEnumerable<T>)
private
FList: TList<T>;
protected
function GenericGetEnumerator: IEnumerator<T>;
public
constructor Create;
destructor Destroy; override;
function GetEnumerator: IEnumerator;
function IEnumerable<T>.GetEnumerator = GenericGetEnumerator;
property List: TList<T> read FList;
end;
недостатком этого является то, что если вы попытаетесь выполнить итерацию с использованием объекта TGenericEnumerable , а не интерфейса, GetEnumerator будет не-общий и вы можете только перебрать объекты TObject.
Обычные предостережения о смешении ссылок на интерфейс и его базовый объект.Если вы ссылаетесь на объект как на тип объекта, так и на IEnumerable <....>, то когда счетчик ссылок на интерфейс возвращается к нулю, объект будет освобожден, даже если у вас все еще есть ссылка на него как на объект,(Вот почему я определил IFungibleTrolls; чтобы я мог ссылаться на коллекцию как на интерфейс).
Вы можете сделать альтернативные реализации TGenericEnumerator ;например, он может содержать ссылку на список, индекс и предикат выбора, которые все предоставлены в конструкторе.