2012-03-26 20 views

Trả lời

9

Các lớp học trong Generics.Collections không triển khai IInterface. Bạn sẽ phải tự giới thiệu nó trong các lớp dẫn xuất của bạn và cung cấp các triển khai chuẩn. Hoặc tìm một nhóm thứ ba khác nhau, tập hợp các lớp chứa để hoạt động.

Ví dụ:

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; 

Sau đó bạn có thể khai báo lớp chuyên ngành của bạn:

TMyList = class(TInterfacedList<IMyItem>, IMyList) 

Hãy nhớ rằng bạn cần phải đối xử với lớp này giống như bất kỳ khác có sử dụng tài liệu tham khảo tính quản lý suốt đời. Chỉ đề cập đến nó thông qua giao diện.

Bạn thực sự muốn thực hiện một số công việc khác trước khi TInterfacedList<T> hữu ích. Bạn cần khai báo một số IList<T> để hiển thị các khả năng của danh sách. Nó sẽ là một cái gì đó như thế này:

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

Bạn có thể sau đó chỉ cần thêm IList<T> vào danh sách các giao diện được hỗ trợ bởi TInterfacedList<T> và lớp cơ sở TList<T> sẽ thực hiện các hợp đồng giao diện.

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

Có an toàn để sao chép các phương pháp của TInterfaceObject không? –

+0

Được rồi. Tôi sẽ cố nhớ điều đó! –

+1

Tôi đã trả lời một chút. Không có nhiều công việc để có được một 'IList ' có khả năng dựa trên 'TList '. –

3

Hãy xem thư viện của Alex Ciobanu Collections. Nó có một loạt các bộ sưu tập chung chung, bao gồm cả thay thế cho các loại Generics.Collections, có thể sử dụng như các giao diện. (Họ đã được thực hiện theo cách đó để tạo điều kiện cho LINQ kiểu ENEX hành vi ông thành lập.)

+0

Tôi ước tôi có thể chấp nhận hơn 1 câu trả lời. Công cụ tuyệt vời mà bạn cung cấp. +1 –

0

Một lựa chọn khác là tạo ra một wrapper được thừa kế từ TInterfacedObject và sử dụng thành phần và đoàn đại biểu cho các chức năng danh sách:

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; 

Gói TList<T>TDictionary<T> mất hơn 500 dòng mã.

Một caveat ... Tôi đã không bao gồm nó ở đây nhưng tôi đã tạo ra một IEnumerable<T> (cũng như liên quan IEnumerator<T>) mà không xuống từ IEnumerable. Hầu hết các thư viện bộ sưu tập của bên thứ ba và OSS đều làm như vậy. Nếu bạn quan tâm đến lý do tại sao this bài đăng trên blog có tổng số tiền khá lớn.

Vâng, thực ra nó không đề cập đến mọi thứ. Ngay cả khi bạn làm việc xung quanh các giao diện bước vào nhau, bạn vẫn sẽ có vấn đề. Một cách thực hiện hoàn hảo khác thoả mãn cả hai giao diện sẽ thành công nếu bạn thực hiện Xây dựng nhưng tiếp tục thất bại với cùng một thông báo lỗi nếu bạn thực hiện một biên dịch gia tăng (ít nhất là trong D2009). Ngoài ra IEnumerator nongeneric buộc bạn trả lại mục hiện tại là TObject. Điều này khá nhiều ngăn cản bạn giữ bất cứ thứ gì trong bộ sưu tập không thể truyền tới TObject.Đó là lý do tại sao không có bộ sưu tập chung nào của tiêu chuẩn thực hiện IEnumerable<T>

+0

Cách này tạo ra nhiều công việc hơn là xuất phát từ TList . –

+0

Vì vậy, thực hiện của riêng bạn từ đầu, đó là những gì tất cả những bộ sưu tập của bên thứ ba làm. Tất cả phụ thuộc vào những gì bạn cần. Nếu bạn cần một bộ sưu tập tính tham chiếu với các ngữ nghĩa của danh sách nhưng bạn muốn giới hạn cách người gọi có thể tương tác với bộ sưu tập, việc gói 'TList ' có thể thích hợp hơn. Tôi chưa bao giờ nói đó là một giải pháp tốt hơn, chỉ là một giải pháp thay thế. –

4

Ngoài nhận xét của tôi ở trên đây, một số giải thích tại sao giao diện chung trong Delphi không hoạt động với guids.

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. 

Bạn thấy rằng nó viết cùng một hướng dẫn cho IList và IList_String1 khi IList nhận được hướng dẫn này và cả hai đều thuộc loại này. Điều này dẫn đến thất bại # 1 vì T không quan trọng khi thực hiện cuộc gọi hỗ trợ. Xác định một bí danh cho các công trình IList (không có lỗi # 2) nhưng không giúp đỡ bởi vì đó vẫn là cùng một guid. Vì vậy, những gì chúng ta cần là những gì đã được thực hiện với IList_String2. Nhưng giao diện đó là không phải được thực hiện bởi TList vì vậy tất nhiên chúng tôi nhận được lỗi # 3.

Điều này đã được báo cáo cách đây lâu: http://qc.embarcadero.com/wc/qcmain.aspx?d=78458

+0

Có cách nào để giải quyết GUID và Hỗ trợ tránh này không? –

+0

Nó phụ thuộc. Vào mùa xuân, chúng tôi quyết định không đặt guids trên các giao diện chung nhưng hầu hết các giao diện liên quan đến bộ sưu tập (như IList ) có giao diện không chung chung hoạt động với TValue. Ngoài ra IEnumerable (là loại cơ sở của tất cả các giao diện liên quan đến bộ sưu tập khác) có một thuộc tính gọi là ElementType, cung cấp cho bạn typeinfo của T. –

+0

@StefanGlienke Tôi đã nghe về mùa xuân và tôi đã tự hỏi bạn sử dụng khung công cụ nào để giả lập các đối tượng trên kiểm tra đơn vị của bạn. Tôi đã không tìm thấy một khuôn khổ giả tốt cho delphi. –

Các vấn đề liên quan