2013-06-21 37 views
6

phép nói rằng tôi có một helper lớp mẫuDelphi lớp Helper RTTI GetMethod

TSampleClassHelper = class helper for TSampleClass 
public 
    procedure SomeHelper; 
end; 

tôi thực hiện như sau:

var 
    obj :TSampleClass; 
begin 
    obj:=TSampleClass.Create; 
    obj.SomeHelper; 
end; 

và các công trình này như mong đợi.

Nhưng làm cách nào tôi có thể sử dụng RTTI để gọi phương thức trợ giúp thay thế? Những điều sau đây dường như không hoạt động, GetMethod trả lại không.

var 
    obj :TSampleClass; 
    ctx :TRTTIContext; 
    rtype :TRTTIType; 
    rmethod :TRTTIMethod; 
begin 
    obj:=TSampleClass.Create; 
    rtype:=ctx.GetType(obj.ClassType); 
    rmethod:=rtype.GetMethod('SomeHelper'); // rmethod is nil ! 
end; 

RTTI không hoạt động cho các phương pháp được xác định trong trình trợ giúp lớp học? Có cách nào để khắc phục điều này?

Cảm ơn.

+0

tôi nhìn thấy, nhưng trong mã thực tế cuộc sống của tôi, tôi đang thử nghiệm cho 'SomeMethod' chống lại bất kỳ đối tượng tùy ý. Tôi không biết nếu đối tượng có phương pháp được xác định thông qua một người trợ giúp hay không.Vì vậy, tôi đoán nó sẽ không làm việc cho 'SomeMethod' được xác định thông qua một trợ giúp lớp. Oh well. –

Trả lời

8

Lý do mã của bạn trả về phương thức nil là loại đối tượng không chứa phương thức có tên SomeHelper. Kiểu chứa phương thức đó là kiểu trình trợ giúp.

Vì vậy, bạn có thể viết này sẽ trả về một phương pháp phi nil:

obj:=TSampleClass.Create; 
rtype:=ctx.GetType(TypeInfo(TSampleClassHelper)); 
rmethod:=rtype.GetMethod('SomeHelper'); 

Tất nhiên, bạn ngay lập tức sẽ thấy vấn đề đầu tiên, cụ thể là việc sử dụng một loại thời gian biên dịch chỉ định, TSampleClassHelper. Chúng tôi có thể sử dụng RTTI để khám phá TSampleClassHelper tại thời gian chạy dựa trên loại cá thể không? Không, chúng tôi không thể, như tôi sẽ giải thích dưới đây.

Thậm chí nếu chúng tôi đặt nó sang một bên, theo như tôi thấy, không có cách nào để gọi phương thức bằng RTTI. Nếu bạn gọi rmethod.Invoke(obj, []) thì mã trong số TRttiInstanceMethodEx.DispatchInvoke chặn một nỗ lực gọi phương thức trợ giúp. Nó chặn nó bởi vì nó quyết định rằng kiểu cá thể không tương thích với lớp của phương thức. Mã thích hợp là:

if (cls <> nil) and not cls.InheritsFrom(TRttiInstanceType(Parent).MetaclassType) then 
    raise EInvalidCast.CreateRes(@SInvalidCast); 

Vâng, bạn có thể lấy địa chỉ mã của phương pháp helper với rmethod.CodeAddress nhưng bạn sẽ cần phải tìm một số cách khác để gọi phương thức đó. Thật dễ dàng để đưa nó vào một phương thức có chữ ký thích hợp và gọi nó. Nhưng tại sao phải bận tâm với rmethod.CodeAddress trong mọi trường hợp? Tại sao không sử dụng TSomeHelperClass.SomeMethod và cắt RTTI ra khỏi vòng lặp?

Thảo luận

độ phân giải Helper được thực hiện tĩnh dựa trên helper hoạt động tại thời điểm biên dịch. Khi bạn cố gắng gọi phương thức trợ giúp bằng RTTI, không có trợ giúp hoạt động. Từ lâu bạn đã hoàn thành việc biên dịch. Vì vậy, bạn phải quyết định lớp trợ giúp nào để sử dụng. Tại thời điểm đó, bạn không cần RTTI.

Vấn đề cơ bản ở đây là độ phân giải phương thức trợ giúp lớp về cơ bản là một quá trình tĩnh được thực hiện bằng ngữ cảnh của trình biên dịch. Vì không có ngữ cảnh trình biên dịch trong thời gian chạy, nên không thể thực hiện độ phân giải phương thức trình trợ giúp lớp bằng RTTI.

Đối với cái nhìn sâu sắc hơn vào này có một chi của Allen Bauer của câu trả lời ở đây: Find all Class Helpers in Delphi at runtime using RTTI?

+0

Nó không phải là có nhiều người giúp đỡ cho một lớp học. Đó là một số lớp có phương thức cần thiết được xác định bởi một trình trợ giúp, một số thì không. Ví dụ cho TStrings tôi đã thêm phương thức thông qua một helper nhưng đối với TMyClass nó được định nghĩa trực tiếp trên lớp. –

+0

@AJ. Vấn đề thực sự là độ phân giải của phương thức trợ giúp được thực hiện một cách tĩnh bởi trình biên dịch dựa trên ngữ cảnh biên dịch. Khái niệm đó hoàn toàn trực giao với RTTI. –