基于Delphi的接口编程入门
为什么使用接口?
举个例子好了:有这样一个卖票服务,电影院可以卖票,歌剧院可以卖票,客运站也可以卖票,那么我们是否需要把电影院、、歌剧院和客运站都设计成一个类架构以提供卖票服务?要知道,连经理人都可以卖票,很显然不适合把经理人也包括到卖票服务的继承架构中,我们需要的只是一个共通的卖票服务。于是,卖票的服务是个接口,电影院、歌剧院什么的只要都遵循这样一个服务定义就能很好地相互交互和沟通(如果需要的话)。
如何在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;
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值
在Delphi7中接口的实现类还必须是从TInterfacedObject继承下来才行,如:
TIntfClass = class(TInterfacedObject, IMyInterface)
4、接口和对象生命期
因为Delphi会自行检查接口,如果在使用后没有释放而在生成的程序里加上释放代码,但也因这样带来了问题,如下面代码:
var
i: Integer;
aObj: TIntfClass;
aIntf: IMyInterface;
begin
aObj := TIntfclass.Create;
try
aIntf := aObj;
aIntf.GetName...
finally
aIntf := nil;
FreeAndNil(aObj);
end;
上面的代码执行的话会产生存取违规错误,是因为对接口置nil时已释放接口,而FreeAndNil(aObj)会再释放aIntf一次,而在对aIntf置
nil时已释放了该对象。解决这个问题只要不让接口干扰对象的生命期就可以了,在Release中只需减引用计数而不做释放的动作。
function TIntfClass._Release: Integer;
begin
Result := InterlockedDecrement(FRefCount);
end;
5、接口的委托(Interface Delegation)
分为两种:
1. 对象接口委托
2. 类对象委托。
. 对象接口委托,假如已有下面接口定义:
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。
相关函数:
GetInterfaceCount; //获取接口数量。
GetInterfaceTable; //获取接口表格。
相关结构:
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;
基于Delphi的接口编程入门的更多相关文章
- 初识Django —Python API接口编程入门
初识Django —Python API接口编程入门 一.WEB架构的简单介绍 Django是什么? Django是一个开放源代码的Web应用框架,由Python写成.我们的目标是用Python语言, ...
- 《Delphi XE6 android 编程入门教程》推荐
近5.6年已经没有看见关于delphi的新技术的书出来了(看来在国内delphi的使用量确实很低了), 高勇同学最近出了一本<Delphi XE6 android 编程入门教程>,上周刚拿 ...
- delphi中接口的委托和聚合
Delphi的TRegistry注册表类 方法详解 Delphi的接口编程入门 delphi中接口的委托和聚合 2009-09-27 10:44:44| 分类: 默认分类 | 标签: |举报 |字 ...
- Matlab与.NET基于类型安全的接口混合编程入门
原文:[原创]Matlab与.NET基于类型安全的接口混合编程入门 如果这些文章对你有用,有帮助,期待更多开源组件介绍,请不要吝啬手中的鼠标. [原创分享]Matlab.NET混编调用Figure窗体 ...
- Eclipse 基于接口编程的时候,快速跳转到实现类的方法(图文)
Eclipse 基于接口编程的时候,要跳转到实现类很麻烦,其实Eclipse已经实现该功能. 只要按照Ctrl键,把鼠标的光标放在要跳转的方法上面,第一个是跳转到接口里面,第二个方法是跳转到实现类的位 ...
- [转载]:Delphi xe7并行编程快速入门
现在多数设备.计算机都有多个CPU单元,即使是手机也是多核的.但要在开发中使用多核的优势,却需要一些技巧,花费时间编写额外的代码.好了,现在可以使用Delphi做并行编程了. 在Delphi.C++ ...
- Delphi xe7并行编程快速入门(三篇)
现在多数设备.计算机都有多个CPU单元,即使是手机也是多核的.但要在开发中使用多核的优势,却需要一些技巧,花费时间编写额外的代码.好了,现在可以使用Delphi做并行编程了. 在Delphi.C++ ...
- Delphi xe7并行编程快速入门(转)
源:http://blog.csdn.net/henreash/article/details/41315183 现在多数设备.计算机都有多个CPU单元,即使是手机也是多核的.但要在开发中使用多核的优 ...
- 完成《Java编程入门》初稿
Java编程入门 现在的运维工程师不但要懂得集合网络.系统管理而且要和开发人员一起调试系统,社会上也需要"复合性"的运维人员,所以需要做运维的也要懂一些开发,知道软件系统接口的调试 ...
随机推荐
- 关于-RegExp
// exec() exec() 查找并返回当前的匹配结果,并以数组的形式返回. // test() test() 方法检索字符串中的指定值.返回值是 true 或 false. // compile ...
- python装饰器(二)
有参装饰器 def outer(flag): def timer(func): def inner(*args,**kwargs): if flag: print('''执行函数之前要做的''') r ...
- Javascript异步编程的4种方法(阮一峰)
转载: http://www.ruanyifeng.com/blog/2012/12/asynchronous%EF%BC%BFjavascript.html 你可能知道,Javascript语言的执 ...
- Linux常用操作分享
Java开发经常遇到的linux相关操作 1.常用的上传下载(Xshell5) 1).get 从远程服务器上下载一个文件存放到本地,如下: 先通过lcd切换到本地那个目录下,然后通过get file ...
- python学习之----Lambda表达式
Lambda 表达式本质上就是一个函数,可以作为其他函数的变量使用:也就是说,一个函 数不是定义成f(x, y),而是定义成f(g(x), y),或f(g(x), h(x)) 的形式. Beautif ...
- 【Python爬虫实战】 使用代理服务器
代理服务器:是一个处于我们与互联网中间的服务器,如果使用代理服务器,我们浏览信息的时候,先向代理服务器发出请求,然后又代理服务向互联网获取信息,再返回给我们使用代理服务器进行信息爬取,可以很好的解决I ...
- 14.纯 CSS 创作一种侧立图书的特效
原文地址:https://segmentfault.com/a/1190000014751037 HTML代码: <div class="books"> <div ...
- mybatis-spring 集成
http://www.mybatis.org/spring/zh/index.html http://www.mybatis.org/mybatis-3/zh/java-api.html 编程API: ...
- Mybatis五(一级二级缓存、第三方缓存)
一级缓存 Mybatis对缓存提供支持,但是在没有配置的默认情况下,它只开启一级缓存,一级缓存只是相对于同一个SqlSession而言.所以在参数和SQL完全一样的情况下,我们使用同一个SqlSess ...
- django之Model类
Model是model的基类,该类的metaclass是modelbase,在生成model类对象时是采用modelbase的.django.setup()时,apps会把app建立app_confi ...