delphi - tanteo - ¿Cómo hacer una iteración de soporte de interfaz correctamente?
resolver ecuaciones con solver excel (2)
¿Cómo puedo exponer este TList de una interfaz, como IEnumerator
o IEnumerator<IFungibleTroll>
? Estoy usando Delphi XE.
Aquí está lo lejos que conseguí:
unit FungibleTrollUnit;
interface
uses
Windows, Messages, SysUtils,
Variants, Classes, Graphics,
Controls, Forms,
Generics.Collections;
type
IFungibleTroll = interface
[''{03536137-E3F7-4F9B-B1F5-2C8010A4D019}'']
function GetTrollName:String;
function GetTrollRetailPrice:Double;
end;
TFungibleTrolls = class (TInterfacedObject,IEnumerable<IFungibleTroll>)
protected
FTrolls:TList<IFungibleTroll>;
public
// IEnumerable
function GetEnumerator:IEnumerator<IFungibleTroll>;//
// function GetEnumerator:IEnumerator; overload;
// find/search app feature requires searching.
// this
function FindSingleItemByName(aName:String;patternMatch:Boolean):IFungibleTroll;
function FindMultipleItemsByName(aName:String;patternMatch:Boolean):IEnumerable<IFungibleTroll>;
function FindSingleItemByIdentifier(anIdentifer:String):IFungibleTroll;// use internal non-visible identifier to find an app.
constructor Create;
property Trolls:TList<IFungibleTroll> read FTrolls; // implements IEnumerable<IFungibleTroll>;??
private
end;
implementation
{ TFungibleTrolls }
constructor TFungibleTrolls.Create;
begin
FTrolls := TList<IFungibleTroll>.Create;
end;
function TFungibleTrolls.FindMultipleItemsByName(aName: String;
patternMatch: Boolean): IEnumerable<IFungibleTroll>;
begin
end;
function TFungibleTrolls.FindSingleItemByIdentifier(
anIdentifer: String): IFungibleTroll;
begin
end;
function TFungibleTrolls.FindSingleItemByName(aName: String;
patternMatch: Boolean): IFungibleTroll;
begin
end;
function TFungibleTrolls.GetEnumerator: IEnumerator<IFungibleTroll>;
begin
result := FTrolls.GetEnumerator;
end;
//function TFungibleTrolls.GetEnumerator: IEnumerator;
//begin
// result := FTrolls.GetEnumerator; // type IEnumerator<IFungibleTroll> or IEnumerator?
//end;
end.
Me quedo atascado en uno de los tres errores que no logro resolver:
[DCC Error] FungibleTrollUnit.pas(26): E2252 Method ''GetEnumerator'' with identical parameters already exists
-o--
[DCC Error] FungibleTrollUnit.pas(19): E2291 Missing implementation of interface method IEnumerable.GetEnumerator
-o-- [DCC Error] FungibleTrollUnit.pas(19): E2291 Missing implementation of interface method IEnumerable.GetEnumerator
Parece que debo declarar dos formas de GetEnumerator, si declaro TFungibleTrolls para implementar IEnumerable, pero parece que no puedo encontrar la manera de hacerlo, ya sea con sobrecargas, o sin sobrecargas, o usando una "cláusula de resolución de método", como esta:
function IEnumerable.GetEnumerator = GetPlainEnumerator; // method resolution clause needed?
function GetEnumerator:IEnumerator<IFungibleTroll>;
function GetPlainEnumerator:IEnumerator;
Esto probablemente parezca un uso bastante básico de IEnumerable, y hacer una iteración de soporte de la Interfaz, y sin embargo, estoy atascado.
Actualización: parece que cuando trato de hacer esto sin declarar primero una List<T>
, estoy cayendo en una grieta causada por el hecho de que IEnumerable<T>
hereda de IEnumerable
, y aún así, en vez de un solo método get enumerator, mi la clase debe proporcionar varias y, como mi clase no es genérica, no puede "asignarse a sí misma" a los requisitos de IEnumerable directamente a menos que use una declaración genérica de la List<T>
. El ejemplo de Marjan funciona cuando se compila en un proyecto (.dproj + .dpr) pero no cuando se integra en un paquete (.dproj + .dpk) y se compila en el IDE. Funciona bien desde la línea de comandos, en un paquete, pero no en el IDE, en un paquete.
No es una respuesta a su pregunta directamente (sigue trabajando en eso), pero esto es lo que hice para obtener un "enumerador interconectado", es decir, una clase interconectada que admite la iteración:
IList<T> = interface(IInterface)
[...]
function GetEnumerator: TList<T>.TEnumerator;
function Add(const Value: T): Integer;
end;
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
constructor TBjmInterfacedList<T>.Create;
begin
inherited;
FList := TList<T>.Create;
end;
destructor TBjmInterfacedList<T>.Destroy;
begin
FreeAndNil(FList);
inherited;
end;
function TBjmInterfacedList<T>.GetEnumerator: TList<T>.TEnumerator;
begin
Result := FList.GetEnumerator;
end;
function TBjmInterfacedList<T>.Add(const Value: T): Integer;
begin
Result := FList.Add(Value);
end;
Y luego puedes hacer cosas como estas:
ISite = interface(IInterface)
...
end;
ISites = interface(IList<ISite>);
...
end;
var
for Site in Sites do begin
...
end;
con implementar clases como:
TSite = class(TBjmInterfacedObject, ISite)
...
end;
TSites = class(TBjmInterfacedList<ISite>, ISites)
...
end;
Actualizar
Ejemplo de fuente del proyecto cargada en http://www.bjmsoftware.com/delphistuff//interfacedlist.zip
Si realmente desea crear una clase que implemente IEnumerable <T>, puede hacerlo así:
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.
Ahora, tu FungibleTrollUnit tendrá un aspecto similar a este:
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.
Tenga en cuenta que la implementación de IEnumerable no funciona, pero IEnumerable <T> sí funciona.
Esto se debe a que, a menos que T esté restringido, no puede convertir una T en un TObject.
Si T es una cadena o un entero, por ejemplo, entonces IEnumerator no tiene un TObject para devolver.
Si T tiene restricciones para ser una clase, puede lograr que la implementación de IEnumerable funcione.
Si constriñes que T sea una interfaz intrasectorial, podría hacer que IEnumerable funcione (Delphi 2010 y después (convertir GenericGetCurrent en una interfaz secundaria, luego en un TObject), pero dudo que sea una ventaja.
Prefiero usarlo sin las restricciones, y lo hago sin poder iterar todo como TObjects.
TGenericEnumerable <T> .GetEnumerator no puede usar FList.GetEnumerator porque TList <T> .GetEnumerator no devuelve un IEnumerator <T>
Aunque puede implementar TGenericEnumerable <T> sin definir TNonGenericEnumerable, así:
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;
la desventaja de hacer esto es que si intenta iterar utilizando el objeto TGenericEnumerable <T>, en lugar de la interfaz, GetEnumerator no será genérico y solo podrá iterar TObjects.
Advertencias habituales sobre la mezcla de referencias a una interfaz y su objeto subyacente. Si se refiere a un objeto como un tipo de objeto y como un IEnumerable <....>, cuando el recuento de referencia de la interfaz vuelve a cero, el objeto se liberará incluso si todavía tiene una referencia como objeto . (Es por eso que definí IFungibleTrolls, para poder referirme a la colección como una interfaz).
Puede realizar implementaciones alternativas de TGenericEnumerator <T>; por ejemplo, podría contener una referencia a una lista, un índice y un predicado de selección, que se suministran todos en el constructor.