小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

Runtime Type Information 運行時類型信息(四)

 zengbj 2011-01-12

四:接口的RTTI


接口也可以應用RTTI,其原理和內(nèi)容都是和對象的相差無幾,在runtime type information中就提到了在接口聲明的時候加上編譯開關(guān){$M+}就可以為接口產(chǎn)生RTTI,由于接口中所有的方法和函數(shù)都是published的,所以接口RTTI中包含了接口的所有方法和函數(shù),同時只要是父接口聲明的時候有{$M+},則從其派生的接口中也都有RTTI,這點和對象是一致的,與對象TPersistent相對應的是IInvokable 。而且,對于接口,并不需要{$METHODINFO ON},僅僅只是{$M+}就可以將所有的函數(shù)信息,包括返回值、參數(shù)等都編譯進RTTI,這些信息的結(jié)構(gòu)和聲明在單元IntfInfo中。


下面這個方法是獲得接口的RTTI,呵呵,打印出來像不像代碼?
function TForm1.GetIntfRTTI(APInfo: Pointer): String;
var
IntfMetaData: TIntfMetaData;
I, J: Integer;
RoutinePrefix, Params: String;
StrList: TStringList;
begin
StrList:= TStringList.Create;
try
IntfInfo.GetIntfMetaData(APInfo, IntfMetaData);
with StrList do
begin
    Clear;
    Append(Format('Unit %s', [IntfMetaData.UnitName]));
    Append(Format(' %s = interface(%s)', [IntfMetaData.Name,IntfMetaData.AncInfo^.Name]));
    Append(Format(' [%s]', [GUIDToString(IntfMetaData.IID)]));
    for I := 0 to Length(IntfMetaData.MDA)-1 do
    begin
   Params:= '';
   for J := 0 to IntfMetaData.MDA[I].ParamCount-1 do
   begin
      Params:= Params+ IntfMetaData.MDA[I].Params[J].Name+':'+IntfMetaData.MDA[I].Params[J].Info^.Name;
      if (J+1)<IntfMetaData.MDA[I].ParamCount then
          Params:= Params+';';
   end;
   if IntfMetaData.MDA[I].ResultInfo<> nil then
      RoutinePrefix:= 'function   '+IntfMetaData.MDA[I].Name+'(%s):'+
          IntfMetaData.MDA[I].ResultInfo^.Name+'; '+CallingConventionName[IntfMetaData.MDA[I].CC]
   else
      RoutinePrefix:= 'procedure   '+
          IntfMetaData.MDA[I].Name+'(%s);'+CallingConventionName[IntfMetaData.MDA[I].CC];
   Append(' '+Format(RoutinePrefix, [Params]));
    end;
    Append(' end;');
end;
Result:= StrList.Text;
finally
StrList.Free;
end;
end;
該方法調(diào)用的形式如下:
MemoIRTTI.Lines.Text:= GetIntfRTTI(TypeInfo(ITest));
通過函數(shù)TypeInfo獲得接口的類型信息。


對于接口函數(shù),也有類似的方法來驅(qū)動,在單元Invoker中TInterfaceInvoker.Invoke方法(參數(shù):Obj,接口的實現(xiàn)對象;IntfMD,接口的運行時信息,通過單元IntfInfo中函數(shù)GetIntfMetaData獲得;MethNum,所要驅(qū)動函數(shù)在TIntfMetaData.MDA數(shù)組中的index,通過單元IntfInfo中函數(shù)GetMethNum獲得;Context,用以傳遞參數(shù)和返回值,其本身是一個列表,包含了實參地址和返回值地址)。
下面就是用Invoke來驅(qū)動接口方法的代碼,接口ITest和實現(xiàn)類TTest如前所聲明:


驅(qū)動函數(shù)ShowMsg,這個依然是最簡單,沒有參數(shù),沒有返回值,但仍然需要創(chuàng)建對象TInvContext。注意,GetMethNum最后一個參數(shù)有默認值,該參數(shù)的表示的意義是,當函數(shù)有overload的時候,需要指明函數(shù)參數(shù)的個數(shù)用以確定需要獲得是哪一個重載函數(shù),若沒有overload,則為-1.
var
IntfMetaData: TIntfMetaData;
InvC: TInvContext;
MIndex: Integer;
begin
IntfInfo.GetIntfMetaData(TypeInfo(ITest), IntfMetaData, True);
InvC:= TInvContext.Create;
try
MIndex:= GetMethNum(IntfMetaData, 'ShowMsg');
InvC.SetMethodInfo(IntfMetaData.MDA[MIndex]);
FIntfInvoke.Invoke(FTest, IntfMetaData, MIndex, InvC);
finally
InvC.Free;
end;
end;


驅(qū)動函數(shù)AddStr,這里有兩個參數(shù)和一個返回值,傳實參的時候,不需要填入隱式參數(shù)Self,TInvContext中填充的是包含實參以及返回值的變量的地址,填充序號從0開始。
var
IntfMetaData: TIntfMetaData;
InvC: TInvContext;
MIndex: Integer;
P1: String;
P2: Integer;
MResult: String;
begin
IntfInfo.GetIntfMetaData(TypeInfo(ITest), IntfMetaData, True);
InvC:= TInvContext.Create;
try
MIndex:= GetMethNum(IntfMetaData, 'AddStr');
InvC.SetMethodInfo(IntfMetaData.MDA[MIndex]);
P1:= 'BBB';
P2:= 3;
InvC.SetParamPointer(0, @P1);
InvC.SetParamPointer(1, @P2);
InvC.SetResultPointer(@MResult);
FIntfInvoke.Invoke(FTest, IntfMetaData, MIndex, InvC);
ShowMessage(MResult);
finally
InvC.Free;
end;
end;


驅(qū)動函數(shù)IncNum,這里函數(shù)原型在聲明的時候,參數(shù)為var,但是在調(diào)用的時候,卻與其他的形式并沒有什么區(qū)別,其中原因在于由于實參填充的是變量的地址,至于實參變量是否需要被改寫,由函數(shù)invoke內(nèi)部判斷。
var
IntfMetaData: TIntfMetaData;
InvC: TInvContext;
MIndex: Integer;
P1: Integer;
begin
IntfInfo.GetIntfMetaData(TypeInfo(ITest), IntfMetaData, True);
InvC:= TInvContext.Create;
try
MIndex:= GetMethNum(IntfMetaData, 'IncNum');
InvC.SetMethodInfo(IntfMetaData.MDA[MIndex]);
P1:= 3;
InvC.SetParamPointer(0, @P1);
FIntfInvoke.Invoke(FTest, IntfMetaData, MIndex, InvC);
ShowMessage(IntToStr(P1));
finally
InvC.Free;
end;
end;


驅(qū)動函數(shù)GetName,有意思的地方來了。還記得在ObjectInvoke中,不能驅(qū)動參數(shù)為對象的這種函數(shù)嗎?但是在這里卻可以了,因為這里傳遞實參不再是使用Variant開放數(shù)組,就不受傳參類型的限制了。只是,這里參數(shù)類型檢查仍然不是很嚴謹,盡管這個函數(shù)是VCL實現(xiàn),例如,對于函數(shù)GetName參數(shù)要求傳入的是TComponent類型,但是如果實參填入的是一個TPersistent的對象,仍然是可以通過類型檢查而調(diào)用到這個函數(shù)的,只是該函數(shù)內(nèi)部在訪問Component.Name屬性的時候,才會拋錯。
var
IntfMetaData: TIntfMetaData;
InvC: TInvContext;
MIndex: Integer;
MResult: String;
begin
IntfInfo.GetIntfMetaData(TypeInfo(ITest), IntfMetaData, True);
InvC:= TInvContext.Create;
try
MIndex:= GetMethNum(IntfMetaData, 'GetName');
InvC.SetMethodInfo(IntfMetaData.MDA[MIndex]);
InvC.SetParamPointer(0, @Self);
InvC.SetResultPointer(@MResult);
FIntfInvoke.Invoke(FTest, IntfMetaData, MIndex, InvC);
ShowMessage(MResult);
finally
InvC.Free;
end;
end;

有一個小技巧,枚舉出程序中所有RegisterClass的類。
var
FFinder: TClassFinder;
begin
FFinder:= TClassFinder.Create();
try
Result:= FFinder.GetClasses(Proc);
finally
FFinder.Free;
end;
其中TClassFinder聲明在Classes單元中,Proc是回調(diào)函數(shù),類型為TGetClass = procedure (AClass:TPersistentClass) of object,在遍歷內(nèi)部的注冊列表的時候循環(huán)調(diào)用回調(diào)函數(shù),在外部實現(xiàn)回調(diào)函數(shù)的時候,判斷每次傳入進來的類是否是所需要,如果需要則記錄保存下來。



關(guān)于RTTI在動態(tài)連接庫中的使用

DLL不能傳遞RTTI,所以在DLL之間、或DLL與exe之間傳遞對象的時候,不能顯示和使用對象的RTTI。如果需要在傳遞對象的時候同時擁有其RTTI,則需要使用package來替代DLL,因為package之間、package與exe之間是共享RTTI。

最后是一個關(guān)于代碼格式的問題,很多年前我和別人打過一個賭,下面兩種書寫格式:
if   then begin

end

if   then
begin

end
哪種更加規(guī)范。于是我在VCL中搜索,發(fā)現(xiàn)其實兩種書寫方式都有,但是明顯后者多得多,究其原因是在Menu->Tools->Editor
Options...->Source Options->Edit Code
Templates中聲明的代碼模板格式是后一種,在輸入的時候只需要鍵入ifb
組合鍵Ctrl+j 就可以生成了,習慣后,使用起來是不是很快捷呢?

以上說明皆是基于Delphi 6/7版本,后繼版本內(nèi)容略有差異,但原理大體相當。


    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多