Delphi中使用比较少的一些语法
本文是为了加强记忆而写,这里写的大多数内容都是在编程的日常工作中使用频率不高的东西,但是又十分重要。
---Murphy
1,构造和析构函数:
a,构造函数:
一般基于TComponent组件的派生类,都应该使用overload关键字进行继承,Delphi中的对象没有什么复合的概念,在设计时,从简便的角度出发
一般都设计为耦合性较强,但是使用简单的派生类即可。构造函数不是必写的,除非“复合”这样的对象实现,当省略构造函数时,会由其父类来实现
新对象的建立。下面是几个常用的写法:
constructor TfmBaseScreen.Create(AOwner: TComponent); //由于参数并没有变化,所以这里在声明的时候可以加override而不是overload
begin
inherited; // Create(AOwner) ; //这里的Create默认是可以省略的。
if AOwner is TPanel then
Self.Width :=TPanel(AOwner).Width;
end;
//-------------------------
constructor TfmHuaPianScan.Create(AOwner: TComponent; AQuery: TADOQuery;
AZDCode: string; AMvOutBillCode : string; MoveOutQuery : TADOQuery); //这个声明时需要增加overload说明
begin
inherited Create(AOwner);
FQuery := AQuery; 这里实现的仅仅是私有变量的初始化,其实更多的应用是派生类成员对象的初始化,并需要在析构函数中进行释放。
FZDCode := AZDCode;
FMvOutBillCode := AMvOutBillCode;
FMoveOutQuery := MoveOutQuery;
end;
//-------------------------
Constructor TDllLoader.Create(strDLLName : String); //这是一个基于TObject的类,由于没有任何成员,所以这里Create什么关键字都不需要带。
Begin
FhDLL := LoadLibrary(strDLLName);
ASSERT(FhDLL <> 0);
End;
b,析构函数
析构函数的使用比构造函数要严格一些,一定要在任何时候都使用override,以保证父类内存空间可以正确的释放。
下面是几个析构函数的写法:
Destructor TDllLoader.Destroy(); //虽然函数体内没有inherited但是声明时一定要加上override.
Begin
If FhDLL <> 0 then
begin
FreeLibrary(FhDLL);
End;
end
//-----------------------------------------
destructor TfmTest.Destroy; //窗体类的析构,一定也要在声明的时候增加override.
begin
temp.Free; //成员的释放应该都安排在inherited之前。
inherited;
end;
当然,在窗体类的构造和析构当中,有很多成员管理方式,可以直接通过消息-->事件模式来实现,并不需要引用类及对象设计模式。
直接调用窗体的OnCreate和OnDestroy就可以完成很多事情。
2,类方法及引用类
a,类方法
实现模式很简单,只需要在定义过程或函数时,增加一个class关键字就可以了,它的地位跟C语言中的静态方法的地位是一样的。它所处理的信息都是与类相关,
不能引用对象成员。
下面是TObject中的一个标准的类方法,用来返回一个类名:
class function TObject.ClassName: ShortString;
b,类中类(引用类)
普通类的语法规则是:
类名=class(父类名)
成员描述;
end;
而类中类的语法是:
类名=Class of 父类名
成员描述;
end;
类中类的父类似乎是其一个对象成员,它可以直接调用其父类的类方法,而类中类定义出的对象,其实是一个类。
我们可以在用类中类定义的对象中,使用构造和析构方法,构造方法可以调用对象的Create也可以调用其类中类的Create,
但是,析构函数不能使用类中类的类方法,只能使用其创建对象的析构。这种方法常用于将类作为参数的使用上,方便
在某一个方法之中,对其不明类(但明其祖类)的对象,进行类方法调用。
下面是一个标准使用的例子,这是Vcl中TApplication所带的方法,虽然名称是创建Form,其实对于任何基于TComponent的
对象都是可以创建的,其实现手法就是类中类:
procedure TApplication.CreateForm(InstanceClass: TComponentClass; var Reference);
var
Instance: TComponent;
begin
// Set flag that TCustomForm constructor can read, so it knows if it's being
// created as a main form or not (required when MainFormOnTaskbar is True)
FCreatingMainForm := (FMainForm = nil) and InstanceClass.InheritsFrom(TForm);
Instance := nil;
try
{$IF DEFINED(CLR)}
Instance := InstanceClass.Create(Self);
Reference := Instance;
{$ELSE}
Instance := TComponent(InstanceClass.NewInstance); //这里是直接用的TComponent对象赋值一个派生类空间,面向对象是允许的
TComponent(Reference) := Instance; //这里的Reference是一个实参,这句用于指向创建的对象
try
Instance.Create(Self); //NewInstance创建的空间只是分配一片空白区域,这里将其头部区域格式化成TComponent
except
TComponent(Reference) := nil;
raise;
end;
{$IFEND}
if (FMainForm = nil) and (Instance is TForm) then //当系统中还没有明确主窗口时,而创建的对象是窗体对象,则在这里将其自动设置为主窗体。
begin
TForm(Instance).HandleNeeded;
FMainForm := TForm(Instance);
if MainFormOnTaskBar then
SetWindowLong(Handle, GWL_EXSTYLE, GetWindowLong(Handle, GWL_EXSTYLE) or WS_EX_NOACTIVATE);
ChangeAppWindow(Handle, not MainFormOnTaskBar, not MainFormOnTaskBar);
end;
finally
if (FMainForm = nil) and (Instance is TForm) then
TForm(Instance).FCreatingMainForm := False;
end;
end;
类方法中的Self,指的是类本身,而不是对象,所以没办法象普通Self那样使用。
一个类必须创建具有其对象状态的成员,才是有意义的类。在我上一家公司中,见到有人卖弄高深的把全局方法都建立在一个类中,用类实现。
这样的类,既没有其多样的对象,在方法实现时,甚至一点与类相关的东西都没有,还不如建立一个公共方法单元。
一个标准的类,也不能仅仅是对一堆封装成员的Get和Set操作,应该在成员的逻辑处理中,简化其应有的方法,又要保留必要的变化细节。
否则的话,还不如定义一个结构体。
3,构造自己的异常,及合理应用
EFileOpenFailed = class(Exception)
public procedure Warning(); virtual; abstract; //这里是纯虚函数的定义,在使用时,还必须再定义一层子异常类。
end;
所有异常类,都应该以E开头,并且从Exception继承。并且,在触发异常时,应该从继承树的枝节点写起,
根节点的异常触发应该写到最后。异常的调用,可以在except包含段中以
[on 异常实例:异常类定义 do]的模式进行调用。再或者,可以用[raise 异常类名.Create(构造参数列表);]来直接抛出异常
try
SimulateError(Button)
except
on E : EFileOpenFailed do
E.Warning();
on E : Exception do
ShowMessage(E.Message);
end;
4,起源于万物之王的消息机制
Delphi跟Windows消息是一脉相承的,或者说是扩展消息。Windows消息到了Delphi被重新转换成固定的结构体(Record),
然后,由Delphi的对象进行分发处理,再由消息函数进行接收运行其对应功能。
对于Delphi,可以说所有的对象都具有消息接收功能,这个需要追溯到对象的最初定义TObject:
TObject = class
public
constructor Create; //构造函数
procedure Free; //用于释放对象
class function InitInstance(Instance: Pointer): TObject; //使用固定地址,格式化一片实例数据,并返回其对象指针。但是这个并不能分配内存,只操作已分配的内存
procedure CleanupInstance; //清空对象本身所指向的区域内存数据,但是并不释放内存
function ClassType: TClass; inline; //返回类类型,这里的inline相当于注释,表示该功能向下兼容,并且可以减少编译文件的大小。
class function ClassName: string; //返回类名
class function ClassNameIs(const Name: string): Boolean; //用类名判断类
class function ClassParent: TClass; //返回父类,如果强制返回一个基于TObject的父类,则会报错。
class function ClassInfo: Pointer; inline; //返回类信息的指针
class function InstanceSize: Longint; inline; //返回基于当前类的实例使用的空间大小
class function InheritsFrom(AClass: TClass): Boolean; //判断是否继承于特定类
class function MethodAddress(const Name: ShortString): Pointer; overload; //根据类所含方法的名称,返回对应指针。
class function MethodAddress(const Name: string): Pointer; overload; //同上
class function MethodName(Address: Pointer): string; //根据方法的地址指针,返回方法名
function FieldAddress(const Name: ShortString): Pointer; overload; //根据属性的名称,返回属性的地址。
function FieldAddress(const Name: string): Pointer; overload; //同上
function GetInterface(const IID: TGUID; out Obj): Boolean; //获得一个接口地址,返回执行成功与否
class function GetInterfaceEntry(const IID: TGUID): PInterfaceEntry; //直接返回一个接口地址
class function GetInterfaceTable: PInterfaceTable; //获取接口表地址
class function UnitName: string; //类定义所在的单元文件名
function Equals(Obj: TObject): Boolean; virtual; //对象比较
function GetHashCode: Integer; virtual; //得到哈希代码
function ToString: string; virtual; //等同ClassName,可多态改动
function SafeCallException(ExceptObject: TObject;
ExceptAddr: Pointer): HResult; virtual; //以安全模式调用异常
procedure AfterConstruction; virtual; //构造后处理
procedure BeforeDestruction; virtual; //析构前预处理
procedure Dispatch(var Message); virtual; //消息分发
procedure DefaultHandler(var Message); virtual; //默认接收方法,可以接收所有编号的消息。
class function NewInstance: TObject; virtual; //开辟一个新对象空间,其地址是由系统自动分配的
procedure FreeInstance; virtual; //释放实例空间
destructor Destroy; virtual; //析构
end;
消息使用的三个步骤,
<1>,给具有消息结构体的消息变量赋值。
<2>,构造一个具有处理消息方法的类。处理消息方法的书写格式:方法名(var 消息参数); message 消息号;
<3>,用一个消息处理类的实例,用Dispatch或者DefaultHandler方法对消息进行分发和处理。这里的分发是指分发到类内部过程。
例程:
TMyMsg = record //消息定义,这里是一种最简单的消息结构体。
Msg : Cardinal;
MsgText : ShortString;
end;
TMsgAccepter = class //接收消息类定义,这里省略了消息方法阐述部分
private
procedure AcceptMsg2000(var msg : TMyMsg); message 2000; //这里定义触发方式及触发消息号
procedure AcceptMsg2002(var msg : TMyMsg); message 2002;
public procedure
DefaultHandler(var Message); override; //这里用于接收其他编号的消息
end;
//调用过程
var
MsgAccept : TMsgAccepter;
Msg :TMyMsg;
begin
MsgAccept := TMsgAccepter.Create;
try
Msg.Msg:=2000;
Msg.MsgText:='消息测试文本';
MsgAccept.Dispatch(Msg); //TObject自带的方法
finally
MsgAccept.Free;
end;
end;
VCL的事件定义都是如此,先在VCL控件中定义出接收特定消息的方法,再由事件属性(这里的属性是一个指针),去指定相关方法,
并在事件(TNotifyEvent)子类中形成用户事件区。
注意:以WM_开头的消息,是Windows定义的消息;以CM_开头的消息,是VCL库自定义的消息。
5,行标的定义Label.
面向对象的编程中并不建议使用行表和goto跳转语句,所以在通常的Delphi代码中很少见,但是并不是说Delphi不具备goto跳转,eg.
var
a,b: Integer;
label
X,Y;
begin
if a > b then
goto X
else
goto Y;
X:
WriteLn('a>b');
Y:
WriteLn('b>a');
end;
6,一个几乎脱离VCL的程序框架:
program Console;
{$APPTYPE CONSOLE} //这里表示支持对用户控制台的操作
//uses SysUtils;
var
str:string;
begin
writeln('您好,这是一个示范程序,请输入一行文字:');
readln(str);
writeln('您输入的是:',str);
Writeln(TObject.classname); //这行代码仅仅表示,可以访问VCL中的System.pas
if TObject.ClassNameIs('TObject') then
Writeln('OK');
readln;
end.
7,回调函数与方法指针:
回调函数是引用C语言中的叫法,在Delphi中,既可以是函数,又可以是过程。
a,简单的函数指针
<1>定义语法:
type
过程类名=procedure ([参数表列]); //注意,这里procedure后没有过程名
函数类名=function([参数表列]) : 返回类型;
end;
<2>用以上定义的方法类可以定义出一个方法指针变量,并且这个变量可以作为其他方法的参数来使用.
回调函数的形参实现,以下是阐述部分事例:
procedure 回调函数名(..., A : 过程类名,...);
begin
...A([参数表列]).. //这里实现了对过程A的使用,使用时不需要考虑A的阐述部分,因为它还没有真正的实体,但是参数表列要跟<1>中定义的一致。
end;
<3>主调函数的使用.
主调函数中,需要定义出一个具有实体的方法,这个方法作为参数使用,替换<2>中的A成为实参。
这个作为实参出现的方法不需要额外的说明其类型,但是必须要与<1>中的参数表列及返回类型一致。
eg.
function 作为实参的方法([参数表列]) : 返回值;
begin
...... //注意,这一部分才是真正的实现了<1>中方法的实体
end;
procedure 一个主调的方法;
begin
回调函数名(..., 作为实参的方法,...); //注意,这里的<2>中的A已经被换成了上面的实体方法。
end;
整个回调函数的实现,是分三步的,前两步分别是定义〖方法指针〗及〖回调函数与方法指针的使用关系〗,第三步
才是真正实现〖方法指针的实体及回调函数的使用〗。
b,与类和对象相关的方法指针:
语法定义跟简单方法指针有点象,不过结尾多了 of object:
过程类名=procedure ([参数表列]) of object;
函数类名=function([参数表列]) : 返回类型 of object;
这类的指针往往是指向一个对象方法,甚至类方法(作为类方法指针,前面要加class)来用。
其指针函数在实际实现时,其实是隐藏了一个self参数的,可以针对对象或者类来进行操作。并且,以of object定义的
指针,不可以与简单方法指针转换和赋值。
这也就是我们为什么在窗体中这么写代码会报错:
button1.onclick=buttonclick; //这里buttonclick虽然单独执行与button1.onclick可能是一样的,但是它仅仅是个本地过程,而onclick是个对象事件。
我们再来看看事件的标准定义:
TNotifyEvent = procedure(Sender: TObject) of object; //这句话其实就是定义了一个方法指针,而OnClick就是这种方法实例。
c,跨工程接口调用的关键字_cdecl, _stdcall,_fastcall,_cdeclspec
这节内容实际是与方法指针没有直接关系的,但是一般使用方法指针时又极易涉及到这些关键字,所以放在一起讲了。
这个下划线是C语言约定的标准,对于不同的语言其使用的方式也不一样。
cdecl 是C Declare 即C语言标准接口。
stdcall 是 standard Call ,这个接口标准被Win API 所采用,也是最为通用的调用标准,例如delphi也用它。
fastcall 在C++Builder当中使用广泛。
cdeclspc 用的比较少,它的直接意思是一个dll引用方法。
所有的关键字与其定义接口的关键字必须一致,其实际意义是这些方法及方法参数的压栈方法,细节我在这里不详述。
下面是一个标准的Delphi外部方法接口的定义:
{要注意,加了关键字的方法名都是区分大小写的。这里的MTASDK就是外部方法名。}
function mta001_Exit(): Integer; stdcall; external MTASDK name '_mta001_Exit';
最后再说明一点,假如定义了一个方法指针变量A,那么@A并不代表A的指针,而且是一个标准类型4字节的Pointer指针。
只有再多加一个@才能代表其方法地址,即:@@A
8,数组,集合与类型别名的使用。
a,数组定义:
var
数组变量 : array [数组起始下标..终止下标] of 元素类型;
允许定义复杂点的数组,多维数组及数组的数组。
数组变量 : array [起始下标1..终止下标1,起始下标2..终止下标2] of 元素类型; //这是一个二维数组.
数组变量 : array [起始下标..终止下标] of array [起始下标..终止下标] of 元素类型。 //这是一个数组的数组。
数组允许在定义变量时省略下标,然后在使用时,再用SetLength来设定数组的长度(即开辟内存),这样定义的数组成为动态数组。
而数组名不带下标时,仅仅等同于一个指针。动态数组的默认起始下标是从0开始的整数。
数组可以通过Low(数组名)和High(数组名)来返回数组的上下标。
b,集合定义:
type
集合名=set of [元素表列]; //元素表列可以是多个元素用逗号隔开,也可以用上下标加..表示,还可以是枚举类型名。
TToolButton = (tbAdd, tbDel, tbEdit, tbPost, tbCancel); //这是一个枚举
TToolBtnSet = set
of TToolButton; //这是一个集合
这个也可以在定义变量的时候直接定义集合:
var
集合变量 : 集合类型=set of [元素表列];
常用的集合运算有in ,并且可以用+/-[元素表列]来增删集合中的元素。
注意:集合的类型实例数是有限制的,必须在255以内。例如:集合类型=set of Char; 这样的定义是合法的,因为Char的实例数刚好255,
而:集合类型=set of string; 或者:集合类型=set of integer; 这样的定义是会报系统错误的。 所以,一般集合引用枚举类型来定义很常见。
c,类型别名:
类型别名 : type 原类型名;
注意,这样定义的变量,在计算和赋值是是完全兼容的,但是在out型参数使用时,不匹配是会报错的。
类型别名的定义,有利于代码的优化和变更。
9,指针与地址内容
Delphi中使用比较少的一些语法的更多相关文章
- 转:Delphi中使用比较少的一些语法
http://www.cnblogs.com/Murphieston/p/5577836.html 本文是为了加强记忆而写,这里写的大多数内容都是在编程的日常工作中使用频率不高的东西,但是又十分重要. ...
- DELPHI语法基础学习笔记-Windows 句柄、回调函数、函数重载等(Delphi中很少需要直接使用句柄,因为句柄藏在窗体、 位图及其他Delphi 对象的内部)
函数重载重载的思想很简单:编译器允许你用同一名字定义多个函数或过程,只要它们所带的参数不同.实际上,编译器是通过检测参数来确定需要调用的例程.下面是从VCL 的数学单元(Math Unit)中摘录的一 ...
- delphi 中sql的语法规范
1.引号配对: 这是在Delphi使用SQL语句时容易出错的地方,由于delphi规定在字符串中用两个西文的单引号“''”表示一个“'”,在拼装语句的时候就容易疏忽遗漏. Delphi里有个函数Q ...
- 如何在 Delphi 中静态链接 SQLite
搞了我几个小时,终于成功在 Delphi 中静态链接了 SQLite (v3.5.4),下一步就是研究加密了,呵呵中间其实遇到很多问题,今天累了,就不说了,改天补上 下载测试工程 下面说说方法 1.当 ...
- 翻箱倒柜,《Delphi中建议使用的语句》
(*//标题:Delphi中建议使用的语句整理:Zswang连接:http://www.csdn.net/Expert/TopicView1.asp?id=724036日期:2002-06-22支持: ...
- Delphi中线程类TThread实现多线程编程2---事件、临界区、Synchronize、WaitFor……
接着上文介绍TThread. 现在开始说明 Synchronize和WaitFor 但是在介绍这两个函数之前,需要先介绍另外两个线程同步技术:事件和临界区 事件(Event) 事件(Event)与De ...
- Delphi中DLL的创建和使用
参考:http://blog.csdn.net/ninetowns2008/article/details/6311663 结合这篇博客:http://www.cnblogs.com/xumenger ...
- 用SPCOMM 在 Delphi中实现串口通讯 转
用Delphi 实现串口通讯,常用的几种方法为:使用控件如MSCOMM和SPCOMM,使用API函数或者在Delphi 中调用其它串口通讯程序.利用API编写串口通信程序较为复杂,需要掌握大量通信 ...
- delphi中表示跳出的有break,continue, exit,abort, halt, runerror
1.break 强制退出循环(只能放在循环中),用于从For语句,while语句或repeat语句中强制退出. 2.continue 用于从For语句,while语句或repeat语句强行结束本次 ...
随机推荐
- 在CentOS系统中使用yum安装指定版本软件的方法
yum默认都是安装最新版的软件,这样可能会出一些问题,或者我们希望yum安装指定(特定)版本(旧版本)软件包.所以,就顺带分享yum安装指定(特定)版本(旧版本)软件包的方法. 过程如下: 假设这里是 ...
- tftp 限制ip 限制ip段 或者多个ip段访问
1 限制单个ip访问 tftp 配置tftp信息 vi /etc/xinetd.d/tftp 在 service tftp配置信息中添加 only_form =ip 重启 service xinet ...
- noi 1.5 43:质因数分解
描述 已知正整数 n 是两个不同的质数的乘积,试求出较大的那个质数. 输入 输入只有一行,包含一个正整数 n.对于60%的数据,6 ≤ n ≤ 1000.对于100%的数据,6 ≤ n ≤ 2*10^ ...
- JS中generater和箭头函数
generater跟函数很像: function* fn(x){ yield x; yield x++; return x;} 如上所示,generater用function*定义,可以用yield返 ...
- 自己写一个swap函数交换任意两个相同类型元素的值 对空指针的使用 字节大小的判断(二)了解原理
验证的代码: #include <stdio.h> int main(){ char c = 'z'; ) + (c << ) + () + 'a'; printf(" ...
- Android 开发基础及环境配置
2011年买了第一部安卓操作系统的手机,当时势头正盛的HTC不可思议(incredible),当时的想法就是想学习下智能手机开发,但是由于各种原因,客观上是公司的项目太忙了,忙于项目管理.团队建设.客 ...
- VIM 常用快捷键
一,光标移动 大家不要觉得光标移动不重要,其实它是基础,更好的光标移动,复制,粘贴,删除等才能更加的得心应手,进入了编辑器里面后,鼠标就不能用了. 光标移动 h 或 向左箭头键(←) 20h或者20( ...
- URL请求工具
工作中有个需求,定期请求多个URL.“定期”采用计划任务实现,请求URL,虽说start url可以实现,但不灵活.自己制作了个专门请求URL的工具,并记录请求结果. 控制台程序代码: class P ...
- WebIM 聊天 Demo
最近 2 个月用业余时间写了一个 IM ,动手之前想了很多,包括前期设计.语言.数据库等,经过了一番思想斗争,最终前台用 Vue.js 展示,Server 使用 node ,数据库使用 MongoDB ...
- Angular初学
简介: angularjs是基本js开发的一个前端类库,主要致力于减轻开发人员在开发Ajax应用过程中的痛苦,适合来做单应用. 客户端模板: Angualr中,模板和数据都会被发送到浏览器中,然后在客 ...