2012-05-18 13 views
20

Tôi đang sử dụng Delphi XE để thực hiện một điều tra viên cho phép lọc các phần tử của danh sách theo loại. Tôi đã nhanh chóng lắp ráp một đơn vị kiểm tra như sau:Danh sách thực hiện Enumerator OfType <T> trong Delphi

unit uTestList; 

interface 

uses Generics.Collections; 

type 
    TListItemBase = class(TObject) 
    end; { TListItemBase } 

    TListItemChild1 = class(TListItemBase) 
    end; 

    TListItemChild2 = class(TListItemBase) 
    end; 

    TTestList<T : TListItemBase> = class; 

    TOfTypeEnumerator<T, TFilter> = class(TInterfacedObject, IEnumerator<TFilter>) 
    private 
    FTestList : TList<T>; 
    FIndex : Integer; 
    protected 
    constructor Create(Owner : TList<T>); overload; 

    function GetCurrent : TFilter; 
    function MoveNext : Boolean; 
    procedure Reset; 

    function IEnumerator<TFilter>.GetCurrent = GetCurrent; 
    function IEnumerator<TFilter>.MoveNext = MoveNext; 
    procedure IEnumerator<TFilter>.Reset = Reset; 
    end; 

    TOfTypeEnumeratorFactory<T, TFilter> = class(TInterfacedObject, IEnumerable) 
    private 
    FTestList : TList<T>; 
    public 
    constructor Create(Owner : TList<T>); overload; 
    function GetEnumerator : TOfTypeEnumerator<T, TFilter>; 
    end; 

    TTestList<T : TListItemBase> = class(TList<T>) 
    public 
    function OfType<TFilter : TListItemBase>() : IEnumerable; 
    end; { TTestList } 


implementation 

{ TOfTypeEnumerator<T, TFilter> } 

constructor TOfTypeEnumerator<T, TFilter>.Create(Owner: TList<T>); 
begin 
    inherited; 
    FTestList := Owner; 
    FIndex := -1; 
end; 

function TOfTypeEnumerator<T, TFilter>.GetCurrent: TFilter; 
begin 
    Result := TFilter(FTestList[FIndex]); 
end; 

function TOfTypeEnumerator<T, TFilter>.MoveNext: Boolean; 
begin 
    Inc(FIndex); 
    while ((FIndex < FTestList.Count) 
     and (not FTestList[FIndex].InheritsFrom(TFilter))) do 
    begin 
    Inc(FIndex); 
    end; { while } 
end; 

{ TOfTypeEnumeratorFactory<T, TFilter> } 

constructor TOfTypeEnumeratorFactory<T, TFilter>.Create(Owner: TList<T>); 
begin 
    inherited; 
    FTestList := Owner; 
end; 

function TOfTypeEnumeratorFactory<T, TFilter>.GetEnumerator: TOfTypeEnumerator<T, TFilter>; 
begin 
    Result := TOfTypeEnumerator<T,TFilter>.Create(FTestList); 
end; 

{ TTestList<T> } 

function TTestList<T>.OfType<TFilter>: IEnumerable; 
begin 
    Result := TOfTypeEnumeratorFactory<T,TFilter>.Create(self); 
end; 

end. 

Biên dịch đơn vị này không thành công với lỗi F2084 Lỗi nội bộ: D7837. Tôi chắc chắn có thể làm điều này mà không có một điều tra viên, nhưng tôi muốn có sẵn một số để làm cho mã nhất quán. Tôi đã có một vấn đề trình biên dịch tương tự khi cố gắng để thực hiện điều này trên đầu trang của Spring4D, nhưng figured tôi sẽ đưa ra một đồng bằng, vanilla Delphi vấn đề ở đây.

Có ai có triển khai thay thế thực sự biên dịch không?

Cảm ơn.

+4

Cùng trong XE2. Gửi đến QC. Generics vẫn còn khá nhiều không sử dụng được. –

+1

Chỉ cần gửi tới QC - # 105719. Cảm ơn. –

+1

http://qc.embarcadero.com/wc/qcmain.aspx?d=105719 –

Trả lời

31

Bạn không muốn sử dụng IEnumerator <T> từ System.pas, hãy tin tôi đi. Điều đó mang lại rất nhiều rắc rối bởi vì nó kế thừa từ IEnumerator và do đó có phương pháp GetCurrent với kết quả khác nhau (TObject cho IEnumerator và T cho IEnumerator <T>).

Better xác định IEnumerator của riêng bạn <T>:

IEnumerator<T> = interface 
    function GetCurrent: T; 
    function MoveNext: Boolean; 
    procedure Reset; 
    property Current: T read GetCurrent; 
end; 

Cùng với IEnumerable. Tôi có thể nói xác định IEnumerable riêng bạn <T>:

IEnumerable<T> = interface 
    function GetEnumerator: IEnumerator<T>; 
end; 

Nếu bạn sử dụng trong TOfTypeEnumerator bạn < T, TFilter > bạn có thể loại bỏ các điều khoản giải quyết phương pháp gây ra ICE.

Khi bạn làm điều đó, bạn sẽ bắt đầu thấy các lỗi trình biên dịch khác E2008, E2089 và một số khác.

  • gọi chỉ được thừa kế trong hàm tạo của bạn cố gắng gọi hàm tạo có cùng chữ ký trong lớp tổ tiên của bạn không tồn tại. Vì vậy, thay đổi nó để kế thừa Tạo.

  • không sử dụng IEnumerable nhưng sử dụng IEnumerable <TFilter> vì đó là những gì muốn bạn để Enumerator hơn

  • không sử dụng các phương pháp và các cặn lắng rằng chỉ được phép cho các đối tượng hoặc chỉ định các hạn chế lớp trên T và TFilter

  • MoveNext cần một quả

Dưới đây là đơn vị biên dịch. Đã làm một bài kiểm tra nhanh chóng và nó dường như làm việc:

unit uTestList; 

interface 

uses 
    Generics.Collections; 

type 
    IEnumerator<T> = interface 
    function GetCurrent: T; 
    function MoveNext: Boolean; 
    property Current: T read GetCurrent; 
    end; 

    IEnumerable<T> = interface 
    function GetEnumerator: IEnumerator<T>; 
    end; 

    TOfTypeEnumerator<T: class; TFilter: class> = class(TInterfacedObject, IEnumerator<TFilter>) 
    private 
    FTestList: TList<T>; 
    FIndex: Integer; 
    protected 
    constructor Create(Owner: TList<T>); overload; 

    function GetCurrent: TFilter; 
    function MoveNext: Boolean; 
    end; 

    TOfTypeEnumeratorFactory<T: class; TFilter: class> = class(TInterfacedObject, IEnumerable<TFilter>) 
    private 
    FTestList: TList<T>; 
    public 
    constructor Create(Owner: TList<T>); overload; 
    function GetEnumerator: IEnumerator<TFilter>; 
    end; 

    TTestList<T: class> = class(TList<T>) 
    public 
    function OfType<TFilter: class>: IEnumerable<TFilter>; 
    end; 

implementation 

{ TOfTypeEnumerator<T, TFilter> } 

constructor TOfTypeEnumerator<T, TFilter>.Create(Owner: TList<T>); 
begin 
    inherited Create; 
    FTestList := Owner; 
    FIndex := -1; 
end; 

function TOfTypeEnumerator<T, TFilter>.GetCurrent: TFilter; 
begin 
    Result := TFilter(TObject(FTestList[FIndex])); 
end; 

function TOfTypeEnumerator<T, TFilter>.MoveNext: Boolean; 
begin 
    repeat 
    Inc(FIndex); 
    until (FIndex >= FTestList.Count) or FTestList[FIndex].InheritsFrom(TFilter); 
    Result := FIndex < FTestList.Count; 
end; 

{ TOfTypeEnumeratorFactory<T, TFilter> } 

constructor TOfTypeEnumeratorFactory<T, TFilter>.Create(Owner: TList<T>); 
begin 
    inherited Create; 
    FTestList := Owner; 
end; 

function TOfTypeEnumeratorFactory<T, TFilter>.GetEnumerator: IEnumerator<TFilter>; 
begin 
    Result := TOfTypeEnumerator<T, TFilter>.Create(FTestList); 
end; 

{ TTestList<T> } 

function TTestList<T>.OfType<TFilter>: IEnumerable<TFilter>; 
begin 
    Result := TOfTypeEnumeratorFactory<T,TFilter>.Create(self); 
end; 

end. 
+0

Cũng nói. Tôi không may gặp lời khuyên của bạn quá muộn; các giao diện trong hệ thống đơn vị là một nỗi đau thực sự để thực hiện! – AdrianGW

1

Một làm việc phiên bản sử dụng system.IEnumerable<T>system.IEnumerator<T>

unit uTestList; 

interface 

uses Generics.Collections; 

type 
    TListItemBase = class(TObject) 
    end; { TListItemBase } 

    TListItemChild1 = class(TListItemBase) 
    end; 

    TListItemChild2 = class(TListItemBase) 
    end; 

    TTestList<T : TListItemBase> = class; 

    TOfTypeEnumerator<T : class; TFilter : class> = class(TInterfacedObject, IEnumerator<TFilter>, IEnumerator) 
    private 
    FTestList : TList<T>; 
    FIndex : Integer; 
    protected 
    constructor Create(Owner : TList<T>); overload; 

    function GetCurrent: TObject; 
    function GenericGetCurrent : TFilter; 
    function MoveNext : Boolean; 
    procedure Reset; 

    function IEnumerator<TFilter>.GetCurrent = GenericGetCurrent; 
    end; 

    TOfTypeEnumeratorFactory<T : class; TFilter : class> = class(TInterfacedObject, IEnumerable<TFilter>, IEnumerable) 
    private 
    FTestList : TList<T>; 
    public 
    constructor Create(Owner : TList<T>); overload; 
    function GetEnumerator : IEnumerator; 
    function GenericGetEnumerator : IEnumerator<TFilter>; 
    function IEnumerable<TFilter>.GetEnumerator = GenericGetEnumerator; 
    end; 

    TTestList<T : TListItemBase> = class(TList<T>) 
    public 
    function OfType<TFilter : TListItemBase>() : IEnumerable<TFilter>; 
    end; { TTestList } 


implementation 

{ TOfTypeEnumerator<T, TFilter> } 

constructor TOfTypeEnumerator<T, TFilter>.Create(Owner: TList<T>); 
begin 
    inherited Create; 
    FTestList := Owner; 
    FIndex := -1; 
end; 

function TOfTypeEnumerator<T, TFilter>.GenericGetCurrent: TFilter; 
begin 
    Result := TFilter(TObject(FTestList[FIndex])); 
end; 

function TOfTypeEnumerator<T, TFilter>.GetCurrent: TObject; 
begin 
    Result := TObject(FTestList[FIndex]); 
end; 

function TOfTypeEnumerator<T, TFilter>.MoveNext: Boolean; 
begin 
    repeat 
    Inc(FIndex); 
    until (FIndex >= FTestList.Count) or FTestList[FIndex].InheritsFrom(TFilter); 
    Result := FIndex < FTestList.Count; 
end; 

procedure TOfTypeEnumerator<T, TFilter>.Reset; 
begin 
    FIndex := -1; 
end; 

{ TOfTypeEnumeratorFactory<T, TFilter> } 

constructor TOfTypeEnumeratorFactory<T, TFilter>.Create(Owner: TList<T>); 
begin 
    inherited Create; 
    FTestList := Owner; 
end; 

function TOfTypeEnumeratorFactory<T, TFilter>.GetEnumerator: IEnumerator; 
begin 
    Result := GenericGetEnumerator; 
end; 

function TOfTypeEnumeratorFactory<T, TFilter>.GenericGetEnumerator: IEnumerator<TFilter>; 
begin 
    Result := TOfTypeEnumerator<T,TFilter>.Create(FTestList); 
end; 

{ TTestList<T> } 

function TTestList<T>.OfType<TFilter>: IEnumerable<TFilter>; 
begin 
    Result := TOfTypeEnumeratorFactory<T,TFilter>.Create(self); 
end; 

end. 

Một thủ tục kiểm tra:

var 
    MyElem: TListItemBase; 
    MyElem1: TListItemChild1; 
    MyElem2: TListItemChild2; 
begin 
    Memo1.Clear; 
    for MyElem in FTestList.OfType<TListItemBase>() do 
    begin 
    Memo1.Lines.Add('----------'); 
    end; 
    for MyElem1 in FTestList.OfType<TListItemChild1>() do 
    begin 
    Memo1.Lines.Add('=========='); 
    end; 
    for MyElem2 in FTestList.OfType<TListItemChild2>() do 
    begin 
    Memo1.Lines.Add('++++++++++'); 
    end; 
Các vấn đề liên quan