2011-11-30 31 views
6

Tôi có nguyên tử lạc lớp này initializer:Kết hợp Generics với các ràng buộc khác nhau

type 
    Atomic<T: IInterface> = class 
    type TFactory = reference to function: T; 
    class function Initialize(var storage: T; factory: TFactory): T; 
    end; 

class function Atomic<T>.Initialize(var storage: T; factory: TFactory): T; 
var 
    tmpIntf: T; 
begin 
    if not assigned(storage) then begin 
    tmpIntf := factory(); 
    if InterlockedCompareExchangePointer(PPointer(@storage)^, PPointer(@tmpIntf)^, nil) = nil then 
     PPointer(@tmpIntf)^ := nil; 
    end; 
    Result := storage; 
end; 

Bây giờ tôi muốn thực hiện cùng một khuôn mẫu cho các đối tượng.

Tôi có thể làm hai trong hai lớp riêng biệt, nhưng tôi thực sự muốn đặt cả hai bộ khởi tạo dưới cùng một ô. IOW, tôi lý tưởng muốn sử dụng điều này là

var 
    o: TObject; 
    i: IInterface; 

Atomic<TObject>.Initialize(o, CreateObject); 
Atomic<IInterface>.Initialize(i, CreateInterface); 

Tôi không tìm được giải pháp nào tốt cho việc này. Ý tưởng duy nhất tôi nhận được là khai báo lớp là Atomic<T> (không có ràng buộc) và sau đó bằng cách nào đó (chưa biết cách) kiểm tra RTTI của T trong thời gian chạy và tiến hành tương ứng.

Tôi không thích ý tưởng này nhiều và tôi đang tìm cách tiếp cận tốt hơn.

Trả lời

4

Dường như bạn không thể chỉ định các ràng buộc của loại "lớp hoặc giao diện". Do đó, giải pháp đơn giản nhất có vẻ là giảm ràng buộc (bạn có thể thực thi nó trong thời gian chạy, sử dụng RTTI).

Đối với phương pháp RTTI, bạn có thể sử dụng TypeInfo chức năng:

uses 
    ..., TypInfo;  

class function Atomic<T>.Initialize(var storage: T; factory: TFactory): T; 
var 
    tmpT: T; 
begin 
    if not assigned(PPointer(@storage)^) then begin 
    tmpT := factory(); 
    if InterlockedCompareExchangePointer(PPointer(@storage)^, PPointer(@tmpT)^, nil) = nil then begin 
     case PTypeInfo(TypeInfo(T))^.Kind of 
     tkInterface: 
      PPointer(@tmpT)^ := nil; 
     tkClass: 
      TObject(tmpT).Free; 
     else 
      raise Exception.Create('Atomic<T>.Initialize: Unsupported type'); 
     end; 
    end; 
    end; 
    Result := storage; 
end; 
+0

Điều này có vẻ hoạt động tốt, cảm ơn! – gabr

+0

Chào mừng, @gabr, tôi rất vui vì tôi có thể trợ giúp! –

4

Một giải pháp mạnh mẽ, đánh máy là để bọc cả hai lớp chung vào một lớp khác để cung cấp không gian tên chung cho các hoạt động

type 
    Atomic = class 
    type 
     Intf<T: IInterface> = class 
     type TFactory = reference to function: T; 
     class function Initialize(var storage: T; factory: TFactory): T; 
     end; 
     Obj<T: class> = class 
     type TFactory = reference to function: T; 
     class function Initialize(var storage: T; factory: TFactory): T; 
     end; 
    end; 

Cách sử dụng sau đó sẽ là:

var 
    o: TObject; 
    i: IInterface; 

Atomic.Obj<TObject>.Initialize(o, CreateObject); 
Atomic.Intf<IInterface>.Initialize(i, CreateInterface); 
+0

Đây là một giải pháp tốt đẹp! –

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