2012-03-26 15 views

Respuesta

9

Las clases en Generics.Collections no implementan IInterface. Tendrá que presentarlo usted mismo en sus clases derivadas y proporcionar las implementaciones estándar. O encuentre un conjunto de clases de contenedor diferente, de terceros para trabajar.

Por ejemplo:

TInterfacedList<T> = class(TList<T>, IInterface) 
protected 
    FRefCount: Integer; 
    function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall; 
    function _AddRef: Integer; stdcall; 
    function _Release: Integer; stdcall; 
end; 

function TInterfacedList<T>.QueryInterface(const IID: TGUID; out Obj): HResult; 
begin 
    if GetInterface(IID, Obj) then 
    Result := 0 
    else 
    Result := E_NOINTERFACE; 
end; 

function TInterfacedList<T>._AddRef: Integer; 
begin 
    Result := InterlockedIncrement(FRefCount); 
end; 

function TInterfacedList<T>._Release: Integer; 
begin 
    Result := InterlockedDecrement(FRefCount); 
    if Result = 0 then 
    Destroy; 
end; 

Luego, puede declarar su clase especializada:

TMyList = class(TInterfacedList<IMyItem>, IMyList) 

Recuerde que usted necesita para tratar esta clase como cualquier otro que utiliza la administración de referencia contado toda la vida. Solo haz referencia a ella a través de interfaces.

Realmente querrías hacer un poco más de trabajo antes de que TInterfacedList<T> fuera útil. Debería declarar un IList<T> que expondría las capacidades de la lista. Sería algo así:

IList<T> = interface 
    function Add(const Value: T): Integer; 
    procedure Insert(Index: Integer; const Value: T); 
    .... etc. etc. 
end; 

continuación, puede simplemente añadir IList<T> a la lista de interfaces soportadas por TInterfacedList<T> y la clase base TList<T> cumpliría el contrato interfaz.

TInterfacedList<T> = class(TList<T>, IInterface, IList<T>) 
+2

¿Es seguro para copiar los métodos de la TInterfaceObject? –

+0

De acuerdo. ¡Trataré de recordar eso! –

+1

He desarrollado la respuesta un poco. No es mucho trabajo conseguir un 'IList ' bastante capaz basado en 'TList '. –

3

Tenga una mirada en Collections biblioteca de Alex Ciobanu. Tiene un grupo de colecciones genéricas, que incluyen reemplazos para los tipos Generics.Collections, que se pueden usar como interfaces. (Se hace así para facilitar el comportamiento Enex al estilo de LINQ creó.)

+0

Ojalá pudiera aceptar más de 1 respuesta. Excelentes herramientas que brindó. +1 –

0

Otra alternativa es crear un envoltorio que hereda de TInterfacedObject y el uso de la composición y la delegación a la funcionalidad de listas:

interface 
type 
    IList<T> = interface<IEnumerable<T>) 
    function Add(const Value: T): Integer; 
    .. 
    end; 

TList<T> = class(TInterfacedObject, IList<T>) 
private 
    FList: Generics.Collections.TList<T>; 
public 
    function Add(const Value:T): Integer; 
    .. 
end; 

implementation 
function TList<T>.Add(const Value:T): Integer; 
begin 
    Exit(FList.Add(Value)); 
end; 

Envoltura TList<T> y TDictionary<T> toma un poco más de 500 líneas de código.

Una advertencia ... no lo incluyó aquí, pero creó un IEnumerable<T> (así como la relacionada IEnumerator<T>) que no descienden de IEnumerable. La mayoría de las bibliotecas de colecciones OSS y de terceros hacen lo mismo. Si está interesado en por qué this blogpost prácticamente lo resume.

Bueno, en realidad no menciona todo. Incluso si trabaja alrededor de las interfaces pisándose el uno al otro, todavía tendrá problemas. De lo contrario, una implementación perfecta que satisfaga ambas interfaces tendrá éxito si lo hace Build pero continúa fallando con el mismo mensaje de error si realiza una compilación incremental (al menos en D2009). Además, el IEnumerator no genérico le obliga a devolver el artículo actual como TObject. Esto prácticamente impide que mantenga algo en una colección que no se puede convertir al TObject.Es por eso que ninguno de los colecciones genéricas estándar implementar IEnumerable<T>

+0

De esta manera se crea mucho más trabajo que el derivado de TList . –

+0

Lo mismo ocurre con la implementación propia desde cero, que es lo que hacen todas esas colecciones de terceros. Todo depende de lo que necesites. Si necesita una colección contada de referencia con semántica de lista, pero desea limitar el modo en que la persona que llama puede interactuar con la colección, puede ser preferible ajustar 'TList '. Nunca dije que fuera una mejor solución, solo una alternativa. –

4

Además de mi comentario anterior aquí alguna explicación de por qué interfaces genéricas en Delphi no funcionan con los GUID.

program Project1; 

{$APPTYPE CONSOLE} 

uses 
    SysUtils; 


type 
    IList<T> = interface 
    ['{41FA0759-9BE4-49D7-B3DD-162CAA39CEC9}'] 
    end; 

    IList_String1 = IList<string>; 

    IList_String2 = interface(IList<string>) 
    ['{FE0CB7A6-FC63-4748-B436-36C07D501B7B}'] 
    end; 

    TList<T> = class(TInterfacedObject, IList<T>) 
    end; 

var 
    list: TList<string>; 
    guid: TGUID; 
begin 
    list := TList<string>.Create; 

    guid := IList<Integer>; 
    Writeln('IList<Integer> = ', guid.ToString); 
    if Supports(list, IList<Integer>) then 
    Writeln('FAIL #1'); 

    guid := IList_String1; 
    Writeln('IList_String1 = ', guid.ToString); 
    if not Supports(list, IList_String1) then 
    Writeln('FAIL #2'); 

    guid := IList_String2; 
    Writeln('IList_String2 = ', guid.ToString); 
    if not Supports(list, IList_String2) then 
    Writeln('FAIL #3'); 

    Readln; 
end. 

ves que escribe el mismo GUID para IList y para IList_String1 como IList tiene este GUID y ambos son de este tipo. Esto da como resultado el Fail # 1 porque T no importa al hacer la llamada de soporte. Definir un alias para IList funciona (sin Fail # 2) pero no ayuda porque sigue siendo el mismo guid. Entonces, lo que necesitamos es lo que se ha hecho con IList_String2. Pero esa interfaz es no implementada por TList así que por supuesto obtenemos Fail # 3.

Esto ha sido reportado desde hace mucho tiempo: http://qc.embarcadero.com/wc/qcmain.aspx?d=78458

+0

¿Hay alguna manera de evitar esto evitando los GUID y las ayudas? –

+0

Depende. En Spring decidimos no poner guids en las interfaces genéricas, pero la mayoría de las interfaces relacionadas con la colección (como IList ) tienen una interfaz no genérica que funciona con TValue. También IEnumerable (que es el tipo base de todas las otras interfaces relacionadas con la colección) tiene una propiedad llamada ElementType que te da el typeinfo de T. –

+0

@StefanGlienke He oído hablar de la primavera y me preguntaba qué marco usas para burlar objetos en tu unidad prueba. No he encontrado un buen marco simulado para Delphi. –

Cuestiones relacionadas