本文是为了加强记忆而写,这里写的大多数内容都是在编程的日常工作中使用频率不高的东西,但是又十分重要。

---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中使用比较少的一些语法的更多相关文章

  1. 转:Delphi中使用比较少的一些语法

    http://www.cnblogs.com/Murphieston/p/5577836.html 本文是为了加强记忆而写,这里写的大多数内容都是在编程的日常工作中使用频率不高的东西,但是又十分重要. ...

  2. DELPHI语法基础学习笔记-Windows 句柄、回调函数、函数重载等(Delphi中很少需要直接使用句柄,因为句柄藏在窗体、 位图及其他Delphi 对象的内部)

    函数重载重载的思想很简单:编译器允许你用同一名字定义多个函数或过程,只要它们所带的参数不同.实际上,编译器是通过检测参数来确定需要调用的例程.下面是从VCL 的数学单元(Math Unit)中摘录的一 ...

  3. delphi 中sql的语法规范

    1.引号配对:  这是在Delphi使用SQL语句时容易出错的地方,由于delphi规定在字符串中用两个西文的单引号“''”表示一个“'”,在拼装语句的时候就容易疏忽遗漏.  Delphi里有个函数Q ...

  4. 如何在 Delphi 中静态链接 SQLite

    搞了我几个小时,终于成功在 Delphi 中静态链接了 SQLite (v3.5.4),下一步就是研究加密了,呵呵中间其实遇到很多问题,今天累了,就不说了,改天补上 下载测试工程 下面说说方法 1.当 ...

  5. 翻箱倒柜,《Delphi中建议使用的语句》

    (*//标题:Delphi中建议使用的语句整理:Zswang连接:http://www.csdn.net/Expert/TopicView1.asp?id=724036日期:2002-06-22支持: ...

  6. Delphi中线程类TThread实现多线程编程2---事件、临界区、Synchronize、WaitFor……

    接着上文介绍TThread. 现在开始说明 Synchronize和WaitFor 但是在介绍这两个函数之前,需要先介绍另外两个线程同步技术:事件和临界区 事件(Event) 事件(Event)与De ...

  7. Delphi中DLL的创建和使用

    参考:http://blog.csdn.net/ninetowns2008/article/details/6311663 结合这篇博客:http://www.cnblogs.com/xumenger ...

  8. 用SPCOMM 在 Delphi中实现串口通讯 转

      用Delphi 实现串口通讯,常用的几种方法为:使用控件如MSCOMM和SPCOMM,使用API函数或者在Delphi 中调用其它串口通讯程序.利用API编写串口通信程序较为复杂,需要掌握大量通信 ...

  9. delphi中表示跳出的有break,continue, exit,abort, halt, runerror

      1.break 强制退出循环(只能放在循环中),用于从For语句,while语句或repeat语句中强制退出. 2.continue 用于从For语句,while语句或repeat语句强行结束本次 ...

随机推荐

  1. firefox 插件 URLRedirector 审核通过

    firefox 插件 URLRedirector 审核通过 前段时间弄的 firefox 插件,昨天通过了审核,已经在 firefox 上可以搜索和安装. 插件用 webextension 写的,代码 ...

  2. Vimium使用快捷键总结

    chrome 快捷键: ctrl+w 关闭当前标签  ctrl+t 新建标签 gg行首 shift+g 行尾 Vimium使用快捷键总结 j, <c-e> : Scroll down k, ...

  3. operator->和operator->*

    ->和->*都是C++中定义的可重载的运算符,其中:->称为成员选择符(member selection),而->*称为成员指针选择符(pointer-to-member se ...

  4. Find linux下

    find 1.作用 find命令的作用是在目录中搜索文件,它的使用权限是所有用户. 2.格式 find [path][options][expression] path指定目录路径,系统从这里开始沿着 ...

  5. Excel 导入 导出 Microsoft

    导出: private void exportExcel() { if (saveFileDialog1.ShowDialog() == DialogResult.OK) { Application. ...

  6. git 学习笔记

    1.创建git仓库 git init 2.添加文件 git add readme.txt 3.修改文件 git add readme.txt 4.提交修改 git commit -m "提交 ...

  7. python实现汉诺塔

    经典递归算法汉诺塔分析: 当A柱子只有1个盘子,直接A --> C 当A柱子上有3个盘子,A上第一个盘子 --> B, A上最后一个盘子 --> C, B上所有盘子(1个) --&g ...

  8. 敏捷项目开源管理软件ScrumBasic(1)

    ScrumBasic 是本人基于Asp.net mvc6 最新的core 1.0写的一个敏捷项目管理软件. 目前只是一个基础版本的功能.只支持1个project. 后期会在这个基础上做扩展和权限管理. ...

  9. lucene中FSDirectory、RAMDirectory的用法

    package com.ljq.one; import java.io.BufferedReader;import java.io.File;import java.io.FileInputStrea ...

  10. Microsoft.Crm.Setup.SrsDataConnector.RegisterServerAction 操作失败

     System.Exception: Microsoft.Crm.Setup.SrsDataConnector.RegisterServerAction 操作失败. ---> System.Ty ...