当前位置:首页 > 网络编程 > 软件语言 > DELPHI > 关于基于Delphi平台的接口编程入门

关于基于Delphi平台的接口编程入门

点击次数:55 次 发布日期:2008-11-09 08:43:34 作者:源代码网
源代码网推荐
广告载入中

为什么使用接口?

举个例子好了:有这样一个卖票服务,电影院可以卖票,歌剧院可以卖票,客运站也可以卖票,那么我们是否需要把电影院、、歌剧院和客运站都设计成一个类架构以提供卖票服务?要知道,连经理人都可以卖票,很显然不适合把经理人也包括到卖票服务的继承架构中,我们需要的只是一个共通的卖票服务。于是,卖票的服务是个接口,电影院、歌剧院什么的只要都遵循这样一个服务定义就能很好地相互交互和沟通(如果须要的话)。

如何在Delphi中使用接口

1、声明接口

IMyInterface = interface(IInterface) //说明(1)

            ["{63E072DF-B81E-4734-B3CB-3C23C7FDA8EA}"] //说明(2)

            function GetName(const str: String): String; stdcall;

            function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall; //说明(3)

            function _AddRef: Integer; stdcall; //使接口引用数加1。

            function _Release: Integer; stdcall;//使接口引用数减1,当小于等于0时作释放动作。

            end; 

说明(1):如果有继续关系则在括号里填父接口,否则省却,如:IMyInterface = interface这样就行。

说明(2):此GUID可选,如果要实现具有COM特性的接口的话则需要加上,Delphi中对于有GUID的接口在运行时在VMT表的预定位置生成接口的信息,如接口方法的定义、方法参数定义能详细信息。

说明(3):接口必须实现这三个函数。

2、接口的实现

接口服务是由类来实现的。

TIntfClass = class(TObject, IMyInterface)

private

 FCounter: Integer;

 FRefCount: Integer;

public

 function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;

 ...

end;

3、获取接口

a. 使用类型转换。 如:

var aIntf: IMyInterface; 软件开发网 www.mscto.com

begin

 aObj := TIntfClass.Create;

try

 aIntf := (IMyInterface(aObj);

 ...

b. 利用Delphi编译器内建机制。 如:aIntf := aObj。

c. 利用对象的QueryInterface方法。如OleCheck(aObj.QueryInterface(IID, aIntf)); 只能存取有GUID的COM接口。

d. 利用as操作符。

使用as操作符必须符合下面条件:

1.接口必须明确地指定是从IInterface接口继承下来。

2.必须拥有GUID值 软件开发网 www.mscto.com

在Delphi7中接口的实现类还必须是从TInterfacedObject继承下来才行,如:

TIntfClass = class(TInterfacedObject, IMyInterface) 软件开发网 www.mscto.com

4、接口和对象生命期

因为Delphi会自行检查接口如果在使用后没有释放而在生成的程序里加上释放代码,但也因这样带来了问题,如下面代码:

var

             i: Integer;

             aObj: TIntfClass;

             aIntf: IMyInterface;

            begin

             aObj := TIntfclass.Create;

             try

              aIntf := aObj;

              aIntf.GetName...

             finally

              aIntf := nil;

              FreeAndNil(aObj);

            end; 软件开发网 www.mscto.com 

上面的代码执行的话会产生存取违规错误,是因为对接口置nil时已释放接口,而FreeAndNil(aObj)会再释放aIntf一次,而在对aIntf置

nil时已释放了该对象。解决这个问题只要不让接口干扰对象的生命期就可以了,在Release中只需减引用计数而不做释放的动作。

function TIntfClass._Release: Integer;

begin

Result := InterlockedDecrement(FRefCount);

软件开发网 www.mscto.com

end;

5、接口的委托(Interface Delegation)

分为两种:

1. 对象接口委托

2. 类对象委托。

3. 对象接口委托,假如已有下面接口定义:

IImplInterface = interface(IInterface)

            function ConvertToUSD(const iNTD: Integer): Double;

            function ConvertToRMB(const iNTD: Integer): Double;

            end;

            接着有一个类实现了该接口:

            TImplClass = class(TObject, IImplInterface)

            private

             FRefCount: Integer;

            public

             function ConvertToUSD(const iNTD: Integer): Double;

             ...

            end;

            implementation

            function TImplClass.QueryInterface(const IID: TGUID; out Obj): HResult;

            begin

            if GetInterface(IID, Obj) then

             Result := 0

            else

             Result := E_NOINTERFACE;

            end;

            function TImplClass._Release: Integer;

            begin

             Result := InterlockedDecrement(FRefCount);

            if Result = 0 then

             Destroy;

            end;

            ... ... 

现在有另外一个类TIntfServiceClass要实现IImplInterface接口,不用重新定义,只须使用上面的TImplClass就可以:

TIntfServiceClass = class(TObject, IImplInterface)

            private

             FImplService: IImplInterface;

             //FSrvObj: TImplClass; //如果是用类对象委托的话

            public

             Constructor Create; overload;

             Destructor Destroy; override;

             Constructor Create(aClass: TClass); overload;

             property MyService: IImplInterface read FImplService implements IImplInterface;

             // property MyService: TImplClass read FSrvObj implements IImplInterface; //如果是用对象委托的话。

            end;

            实现如下:

            constructor TIntfServiceClass.Create;

            begin

             FImplService := TImplClass.Create;

            end;

            constructor TIntfServiceclass.Create(aClass: TClass);

            var

             instance: TImplClass;

            begin

             instance := TImplClass(aClass.NewInstance);

             FImplService := instance.Create;

            end;

            destructor TIntfServiceClass.Destroy;

            begin

             FImplService := nil; //遵照TImplClass使用引用计数来控制对象生命周期,看TImplClass的Destroy实现。

             inherited;

            end;  

6、接口和RTTI

Delphi中在VMT-72位移处定义了接口哥格指针:vmtIntfTable = -72。

相关函数: 软件开发网 www.mscto.com

GetInterfaceCount; //获取接口数量。

GetInterfaceTable; //获取接口表格。

软件开发网 www.mscto.com

相关结构:

软件开发网 www.mscto.com

TInterfaceEntry = packed record

            IID: TGUID;

            VTable: Pointer;

            IOffset: Integer;

            ImplGetter: Integer;

            end;

            PInterfaceTable = ^TInterfaceTable;

            TInterfaceTable = packed record

            EntryCount: Integer;

            Entries: array[0..9999] of TInterfaceEntry;

            end;

            Self是指向VMT指针的指针,所以:Self.GetInterfaceTable.EntryCount等价于:

            aPtr := PPointer(Integeer((Pointer(Self))^)   vmtIntfTable)^;

            只要在声明中使用M /M-指令就能在Delphi中编译出的程序里添加RTTI信息,如:

            {$M }

            iInvokable = interface(IInterface)

            {$M-}

            接口的RTTI信息由TIntfMetaData记录结构定义:

            TIntfMetaData = record

            name: String; //接口名称

            UnitName: String; //接口声明的程序单元名称

            MDA: TIntfMethEntryArray; //储存接口中方法信息的动态数组

            IID: TGUID; //接口的GUID值

            Info: PTypeInfo; //描述接口信息的指针

            AncInfo: PTypeInfo; //描述父代信息的指针

            NumAnc: Integer; //此接口继承自父代接口的方法数目

            end;

            TIntfMethEntryArray的定义如下:

            type

             TCallConv = (ccReg, ccCdecl, ccPascal, ccStdCall, ccSafeCall);

             TIntfMethEntry = record

             Name: String; //方法名称

             CC: TCallConv; //调用惯例

             Pos: Integer; //方法在接口中的位置

             ParamCount: Integer; //方法的参数数目

             ResultInfo: PTypeInfo; //描述方法回传类型的信息指针

             SelfInfo: PTypeInfo; //描述方法本身的信息指针

             Params: TIntfParamEntryArray; //描述参数信息的动态数组

             HasRTTI: Boolean; //这个方法是否拥有RTTI信息的布尔值

            end;

            TIntfMethEntryArray = array of TIntfMethEntry;

            参数信息TIntfParamEntry定义:

            TIntfParamEntry = record

            Flags: TParamFlags;

            Name: String;

            Info: PTypeInfo;

            end;

            TTypeInfo = record

            Kind: TTypeKind; //数据类型

            Name: ShortString; //类型信息的字符串格式

            end; 


源代码网推荐

源代码网供稿.
网友评论 (0)
会员中心
网络编程
本站推荐
网络编程之精华