2009-04-30 33 views
13

Kể từ khi câu hỏi của tôi từ yesterday có lẽ không hoàn toàn rõ ràng và tôi đã không nhận được câu trả lời tôi muốn, tôi sẽ cố gắng xây dựng nó một cách tổng quát hơn:hành vi có điều kiện dựa trên kiểu dữ liệu cụ cho generic class

Có một cách để thực hiện hành vi đặc biệt dựa trên loại thực tế của một loại generic instantiated hoặc bằng cách sử dụng explict báo cáo có điều kiện hoặc sử dụng một số loại chuyên ngành? Giả:

TGenericType <T> = class 
    function Func : Integer; 
end; 
... 
function TGenericType <T>.Func : Integer; 
begin 
    if (T = String) then Exit (0); 
    if (T is class) then Exit (1); 
end; 
... 
function TGenericType <T : class>.Func : Integer; 
begin 
Result := 1; 
end; 
function TGenericType <String>.Func : Integer; 
begin 
Result := 0; 
end; 

Trả lời

19

Bạn có thể quay lại RTTI, bằng cách sử dụng TypeInfo(T) = TypeInfo(string). Để kiểm tra xem có gì đó là một lớp hay không, bạn có thể sử dụng một cái gì đó như PTypeInfo(TypeInfo(T))^.Kind = tkClass.

Loại PTypeInfotkClass thành viên điều tra được xác định trong đơn vị TypInfo.

+1

+1 Cảm ơn rất nhiều! Điều đó thực hiện chính xác những gì tôi đang tìm kiếm! Ngay cả khi bạn không phải là người hâm mộ ý tưởng ban đầu của tôi;) – jpfollenius

1

trong C#, bạn có thể làm một typeof(T) mà sẽ cho phép bạn làm điều gì đó như

(T = String) 

hoặc

(T is class) 

tôi havent thấy câu hỏi khác (bạn didnt liên kết của bạn với nó), nhưng bạn là gì thực sự tìm kiếm? Nói chung, làm điều gì đó có điều kiện về loại hoặc một typecode thông qua ifs như bạn đang làm hoặc chuyển đổi nói chung là tốt nhất chuyển thành có một giao diện hoặc chức năng trừu tượng một nơi nào đó được tùy chỉnh theo ngữ cảnh.

+0

Tôi đã thêm liên kết vào câu hỏi khác. Về cơ bản tôi muốn một số ước tính sơ bộ cho kích thước tính theo byte. Tôi muốn xử lý các chuỗi khác với các loại khác. – jpfollenius

+0

Bất kỳ ai đã bỏ phiếu này, hãy bình luận về lý do của bạn. Tôi nghĩ rằng đó là một câu trả lời hợp lý – jpfollenius

+0

Ah, ye olde SO hệ thống nghiệp đôi khi ngạc nhiên, cảm ơn! Amusingly, 'twas tôi rằng upvoted trả lời của bạn-to-self (nhưng đã không upvote câu hỏi ban đầu: P) –

3

Nếu ai đó quan tâm làm thế nào tôi đã thực hiện "Quy mô trường hợp xấu nhất với ưu đãi đặc biệt cho các chuỗi" tôi

class function RTTIUtils.GetDeepSize <T> (Variable : T) : Integer; 
var 
    StringLength   : Integer; 
    Ptr     : PInteger; 
begin 
if (TypeInfo (T) = TypeInfo (String)) then 
    begin 
    Ptr := @Variable; 
    Ptr := PInteger (Ptr^); 
    Dec (Ptr); 
    StringLength := Ptr^; 
    Result := StringLength * SizeOf (Char) + 12; 
    end 
else 
    Result := 0; 
end; 

Đối với tôi, điều này không được công việc trong tầm tay. Cảm ơn tất cả những người đóng góp!

1

TypeInfo (T) là đúng cách. Hơn nữa, bạn có thể sử dụng tất cả các công cụ từ đơn vị TypInfo như bản ghi TTypeData để xác định một số thuộc tính cụ thể của một loại mà bạn sử dụng thay vì chung chung. Khi bạn xác định loại hiện tại được sử dụng thay cho T, bạn có thể sử dụng mẹo con trỏ để nhận giá trị của một biến.

Dưới đây là mã mẫu chấp nhận bất kỳ loại liệt kê nào là chung chung. Lưu ý rằng nó sẽ làm việc cho enumerations thông thường chỉ (không có giá trị cố định như

TEnumWontWork = (đầu tiên = 1, thứ hai, thứ ba)

) và enum phải không được khai báo là kiểu địa phương bên trong một thủ tục. Trong những trường hợp này trình biên dịch tạo ra không có TypeInfo cho enums.

type 
    // Sample generic class that accepts any enumeration type as T 
    TEnumArr<T> = class 
    strict private 
    fArr: array of Byte; 
    fIdxType: TOrdType; 
    function IdxToInt(idx: T): Int64; 
    procedure Put(idx: T; Val: Byte); 
    function Get(idx: T): Byte; 
    public 
    constructor Create; 
    property Items[Index: T]: Byte read Get write Put; default; 
    end; 

constructor TEnumArr<T>.Create; 
var 
    pti: PTypeInfo; 
    ptd: PTypeData; 
begin 
    pti := TypeInfo(T); 
    if pti = nil then 
    Error('no type info'); 
    // Perform run-time type check 
    if pti^.Kind <> tkEnumeration then 
    Error('not an enum'); 
    // Reach for TTypeData record that goes right after TTypeInfo record 
    // Note that SizeOf(pti.Name) won't work here 
    ptd := PTypeData(PByte(pti) + SizeOf(pti.Kind) + (Length(pti.Name)+1)*SizeOf(AnsiChar)); 
    // Init internal array with the max value of enumeration 
    SetLength(fArr, ptd.MaxValue); 
    // Save ordinal type of the enum 
    fIdxType := ptd.OrdType; 
end; 

// Converts index given as enumeration item to integer. 
// We can't just typecast here like Int64(idx) because of compiler restrictions so 
// use pointer tricks. We also check for the ordinal type of idx as it may vary 
// depending on compiler options and number of items in enumeration. 
function TEnumArr<T>.IdxToInt(idx: T): Int64; 
var 
    p: Pointer; 
begin 
    p := @idx; 

    case fIdxType of 
    otSByte: Result := PShortInt(p)^; 
    otUByte: Result := PByte(p)^; 
    otSWord: Result := PSmallInt(p)^; 
    otUWord: Result := PWord(p)^; 
    otSLong: Result := PLongInt(p)^; 
    otULong: Result := PLongWord(p)^; 
    end; 
end; 

function TEnumArr<T>.Get(idx: T): Byte; 
begin 
    Result := fArr[IdxToInt(idx)]; 
end; 

procedure TEnumArr<T>.Put(idx: T; Val: Byte); 
begin 
    fArr[IdxToInt(idx)] := Val; 
end; 

Mẫu sử dụng:

type 
    TEnum = (enOne, enTwo, enThree); 
var 
    tst: TEnumArr<TEnum>; 
begin 
    tst := TEnumArr<TEnum>.Create; 
    tst[enTwo] := $FF; 
    Log(tst[enTwo]); 

Là một sơ yếu lý lịch, tôi đã sử dụng ba thủ thuật ở đây:

1) Bắt TypeInfo cho T với đạo cụ chung của T

2) Bắt TypeData cho T với các đạo cụ chi tiết của T

3) Sử dụng phép thuật con trỏ để có được giá trị của parame ters được cho là loại T.

Hy vọng trợ giúp này.

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