2009-11-06 35 views
11

Tôi đã bắt đầu sử dụng Generics trong Delphi 2010 nhưng tôi có một vấn đề khi biên dịch đoạn mã này:Tại sao các loại TGeneric <Base> và TGeneric <Descendant> không tương thích?

TThreadBase = class(TThread) 
... 
end; 

TThreadBaseList<T: TThreadBase> = class(TObjectList<T>) 
... 
end; 

TDataProviderThread = class(TThreadBase) 
... 
end; 

TDataCore = class(TInterfacedObject, IDataCore) 
private 
    FProviders: TThreadBaseList<TDataProviderThread>; 
... 
end; 

Sau đó, tôi có một số thủ tục lồng nhau:

procedure MakeAllThreadsActive(aThreads: TThreadBaseList<TThreadBase>); 
begin 
... 
end; 

Và cuối cùng tôi muốn gọi thủ tục lồng nhau này trong mã của lớp TDataCore:

MakeAllThreadsActive(FProviders); 

Nhưng biên dịch không muốn để biên dịch nó và nó nói (' <>' dấu ngoặc được thay thế bằng '()'):

[DCC Lỗi] LSCore.pas (494): E2010 loại không tương thích: 'TThreadBaseList (TThreadBase)' và 'TThreadBaseList (TDataProviderThread)'

Tôi không hiểu nó mặc dù TDataProviderThread là hậu duệ của TThreadBase.

tôi phải sửa chữa nó bằng cách typecasting cứng:

MakeAllThreadsActive(TThreadBaseList<TThreadBase>(FProviders)); 

Không ai biết lý do tại sao các trình biên dịch cho biết lỗi này?

+1

Vì những người khác đã giải thích TẠI SAO bạn gặp lỗi này, hãy thử làm cho MakeAllThreadsThực hiện phương thức TThreadBaseList để giải quyết vấn đề. –

Trả lời

22

TDataProviderThread là hậu duệ của TThreadBase, nhưng TThreadBaseList<TDataProviderThread> không phải là hậu duệ của TThreadBaseList<TThreadBase>. Đó không phải là thừa kế, nó được gọi là hiệp phương sai, và mặc dù nó có vẻ giống như điều tương tự, nó không phải và nó phải được hỗ trợ riêng biệt. Hiện tại, Delphi không hỗ trợ nó, mặc dù hy vọng nó sẽ được phát hành trong tương lai. Đây là lý do cơ bản cho vấn đề hiệp phương sai: Nếu chức năng bạn chuyển nó đến là mong đợi một danh sách các đối tượng TThreadBase, và bạn chuyển nó một danh sách các đối tượng TDataProviderThread, không có gì để giữ nó khỏi gọi .Thêm và dán một số đối tượng TThreadBase khác vào danh sách không phải là một TDataProviderThread, và bây giờ bạn đã có tất cả các loại vấn đề xấu xí. Bạn cần các thủ thuật đặc biệt từ trình biên dịch để đảm bảo điều này không thể xảy ra, nếu không bạn sẽ mất an toàn kiểu của mình.

EDIT: Đây là một giải pháp khả thi cho bạn: Hãy MakeAllThreadsActive thành một phương pháp chung chung, như thế này:

procedure MakeAllThreadsActive<T: TThreadBase>(aThreads: TThreadBaseList<T>); 

Hoặc bạn có thể làm những gì Uwe Raabe gợi ý. Hoặc là một trong những sẽ làm việc.

+2

+1 giải thích tốt, mặc dù tôi biết thuật ngữ * hiệp phương sai * chủ yếu từ các phương pháp. – jpfollenius

+2

Nếu bạn chỉ đọc từ đối tượng, thì đó là một ** nguồn và hiệp phương sai là OK.Nếu bạn đang viết giá trị mới thay vào đó, sau đó bạn có ** sink ** và bạn muốn contravariance. Nếu đó là cả nguồn và bồn rửa (chúng ta phải giả định TObjectList là, nếu chúng ta không có kiến ​​thức nội tại về bất kỳ phương thức nào của nó), thì bạn không thể có bất kỳ phương sai nào cả. Imbuing trình biên dịch với khái niệm đó cũng vượt ra ngoài "thủ thuật đặc biệt." –

+1

Các thủ thuật đặc biệt mà tôi đang nói đến liên quan đến việc thêm hỗ trợ cho hiệp phương sai và đối nghịch với trình biên dịch, và sau đó đánh dấu các phương thức riêng lẻ của TObjectList như an toàn cho hiệp phương sai hoặc contravariance theo cách sao cho trình biên dịch có thể xác minh nó. –

6

Loại

TList <TBase> 

không phải là loại mẹ của

TList <TChild> 

Generics không thể được sử dụng theo cách đó.

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