Delphi面向对象的编程方法

Delphi面向对象的编程方法
Delphi面向对象的编程方法

Delphi的编程语言是以Pascal为基础的。Pascal语言具有可读性好、编写容易的特点,这使得它很适合作为基础的开发语言。同时,使用编译器创建的应用程序只生成单个可执行文件(.EXE),正是这种结合,使得Pascal成为Delphi这种先进开发环境的编程语言。

本章中,我们将讨论Object Pascal的主要特点,并讲解如何在事件处理过程和其他应用程序中,使用它来编制程序代码。本章将讲解Delphi应用程序中最常用的Object P ascal语法,而不是Pascal语言的一切细节。如果您完全不熟悉Pascal编程,请参阅一些基础的Pascal教程。如果您具有编程经验,并能熟练地使用其他流行程序语言,您将在本章的Object Pascal中发现一些相同的概念。如果您已经熟悉了Borland Pascal,就可以快速浏览或跳过本章。

2.1 编写Object Pascal程序代码

在前边的章节中,我们通过例程,已经编写了几行简单的代码。在本章中,我们将从熟悉Pascal编程的角度,配合实例,讲解Object Pascal编程的基本方法。

在编写自己的Object Pascal程序时,要注意程序的可读性。Pascal语言是英式结构语言,在程序中选择合适的缩排、大小写风格,并在需要时将程序代码分行,会使得程序代码能够很容易地被自己和他人读懂。一般的程序员都有这样的体验:如果不给程序加上适当的注解,一段时间后,自己也难以理清程序的流程。给程序及时地加上注释是良好的编程习惯。Delphi的注释需要加注在{}之间,编辑器会把它们处理成为空白。Delphi保留了Borland Pascal编辑器的风格,关键字采用黑体字,被注释的部分会变暗,这使得编程风格良好,易读易写。

2.1.1 编写赋值语句

在事件处理过程中,最常用到的工作就是把一个新值赋给一个属性或变量。在设计用户界面时,可以使用Object Inspector(Object Inspector)来改变其属性;但有时需要在程序执行时改变属性的值,而且有些属性只能在执行时改变,这些属性在Delphi的在线帮助的“Proprety”主题中被标为执行期属性。进行这种改变,就必须使用赋值语句。

下文的赋值语句表征一个OnClick事件。当按钮按动后,将编辑框部件Edit1的Co lor属性置为clRed:

procedure TForm1.Button1Click(Sender: TObject);

begin

Edit1.Color := clRed;

end;

当按动按钮后赋值语句被执行,编辑框变成红色。

在语句中,部件的名称在属性前,中间用“.”表示属性的所属关系。这样就准确地指定了要将clRed值赋给哪一部件的哪一属性。赋值号为“:=”,不论给属性还是给变量赋值,都是将右边的值赋给左边的属性或变量。

当将一个属性值、变量、常量或文本数据赋给属性或变量时,所赋值的类型和接受此值的属性或变量的类型应相同或兼容。一个属性或变量的类型定义了此属性或变量的可能值集合,也定义了程序代码可以执行的运算。在前边的例程中,编辑框部件的Color属性和clRed的类型都是TColor。可以在在线帮助中找到一个属性的类型;另外一种方法是在O bject Inspector中选定该属性值段,并按下F1键,则类型将在属性说明的结尾处列出,例如Color属性列出下边的语句:

Property Color : TColor;

有些属性是只读(Read Only)的,它们只能被读取,不能被改变。请查阅在线帮助,在Delphi中这些只读属性都有注解。

2.1.2 标识符的说明与使用

标识符是Delphi应用程序中一些量的名称,这些量包括变量(var)、常量(const)、类型(type)、过程(procedure)、方法(Method)及其他,Object Pascal 在应用标识符时,必须首先说明它们。Object Pascal是强类型语言,它的编译器可以检查确保赋给变量或属性的值是正确的类型,以便于您改正错误。因为Object Pascal是编译语言,所以Delphi 的执行速度要比使用解释语言快得多。在使用标识符前说明它们,可以减少程序错误并增加代码的效率。

2.1.2.1 变量

变量是程序代码中代表一个内存地址的标识符,而此地址的内存内容在程序代码执行时可以被改变。在使用变量前必须对它进行说明,即对它进行命名,并说明它的类型。在所有变量说明以前加上保留字var。变量说明左边是变量的名称,右边则是该变量的类型,中间用(:)隔开。

var

Value ,Sum : Integer;

Line : String;

在窗体中加入一个名称为Edit1的编辑框,再加入一个名称(属性Name)为Add的按钮部件,并建立如下的事件处理过程:

procedure TForm1.addClick(Sender: TObject);

var

X , Y: Integer;

begin

X := 100;

Y := 20;

Edit1.Text := IntToStr(X + Y);

end;

在本例中,当按动ADD按钮时,编辑框中显示值120。在Object Pascal中,必须确保变量或属性被赋予类型相同或兼容的值。您可以尝试将赋给X的值改为100.0,或去掉IntToStr函数,在编译时会出现类型不匹配的错误,这也说明了Object Pascal强类型语言的特点。

2.1.2.2 预定义类型

Object Pascal有多个预定义的数据类型,您可以说明任何这些类型的变量:

整形:Integer的范围是-32768到32767,占2字节的内存;Shortint从-128到1 27,占1字节内存;Longint从-2147443648到2147483647 占4字节内存;Byte从0到25 5,占1字节;Word从0到65535,占2字节内存。它们都是没有小数部分的数字。

实型:Single可以包含7到8位有效小数部分,占用4字节的内存;Double类可以包含15到16位有效小数部分,占用8字节的内存;Extended类型包含19到20位有效小数部分,占用10字节内存;Comp可以包含19到20位有效小数部分,占用8字节内存。以上实数类型只有在8087/80287选项[N+]打开才可以使用。Real可以包含11到12位有效小数部分,占用6字节内存。它只有在和以前Borland Pascal兼容的情况下才使用,否则应使用Double或Extended。

布尔型:Boolean,只包含true或False两个值,占用1字节内存。

字符型:Char,一个ASCII字符;字符串类型String一串最长可达255个ASCII 字符。

指针型:Pointer,可以指向任何特定类型。

字符串型:PChar,是一个指向以零结尾的字符串的指针。

除了预定义类型外,Delphi还有自行定义的类型。上述例程的TColor就是这种类型。此外,用户还可以定义自己的数据类型,这部分内容将在下文中详细讲述。

整型类别和实型类别都各有五种类型,同一类别中,所有的类型与其他同类别的都相容,您可以将一种类型的值赋给相同类别中不同类型的变量或属性,而只需要这个值的范围在被赋值的变量或属性的可能值范围内。例如,对于一个Shortint型的变量,可以接受在-128到127范围内的任意整数,例如Shortint类型的7;您不能将300赋给它,因为30 0已经超出了Shortint的范围了。将范围检查功能打开(选用Options|Project,并在Comp iler Options Page中选择Range Checking),将会检查出一个范围错误;如果Range Chec king没有被打开,那么程序代码将可以执行,但被赋值的值将不是您期望的值。

在一些情况下,您可以进行不同类型的变量或属性的赋值。一般来说,可以将一个较小范围的值赋给一个较大范围的值。例如,您可以将整型值10赋给一个接受实型值的Do uble属性而使得值成为10.0,但如果将一个Double类型的值赋给整形变量,则会出现类型错误。如果您不清楚类型的兼容性,可以参阅Delphi的在线帮助中“Type Compatibility and Assignment Compatibility”主题。

2.1.2.3 常量

常量在说明时就被赋予了一个值,在程序执行过程中是不可改变的。下面的例子说明了三个常量:

const

Pi = 3.14159;

Answer = 342;

ProductName = "Delphi";

象变量一样,常量也有类型。不同的是,常量假设其类型就是常量说明中其所代表的值的类型。上文的三个常量的类型分别是real型、整形、字符串型。常量用“= " 表示两边的值是相等的。

2.1.3 过程与函数

过程与函数是程序中执行特定工作的模块化部分。Delphi的运行库包含许多过程与函数以供您的应用程序调用。您不必了解过程与函数的逻辑,但要知道过程与函数的用途。在对象中说明的过程和函数称为方法(Method)。所有的事件处理过程都是过程,以保留字p rocedure开头。每一个事件处理过程只包含了当这一事件发生时需要执行的程序代码。在事件处理过程中使用Delphi已经存在的过程与函数,只需在程序代码中调用它们即可。

2.1.

3.1 一个调用Delphi方法的简单例程

下文将通过对一个Memo部件的文本进行剪切、拷贝、粘贴、清除等编辑的应用程序编制,介绍使用Delphi过程和函数的调用方法。

Memo(备注)部件有一个CutToClipboard方法,实现将用户在memo中选择的文本移到剪贴板上去。由于这个功能已经被建立在此方法中了,所以您只需知道这个方法做什么以及如何使用它即可。

下面的语句表明如何调用一个名为Memo1的memo部件的CutToClipboard方法:

Memo1.CutToClipboard;

通过指定Memo1的名称,说明调用哪一个部件的CutToClipboard方法。如果不指明对象名称,Delphi会显示Unknown identifier错误。当该事件处理过程被触发,程序会执行CutToclipboard中的语句,将Memo1中的文本剪贴到剪贴板上去。

下文的例程展示了如何调用Delphi的方法,实现将备注部件的文本信息剪切、拷贝到剪贴板上;将剪贴板上的标记文本粘贴到备注中,清除备注部件中的全部文本等四个功能。

打开一个新的空窗体,加入一个memo部件和四个按钮,并排列整齐。改变按钮部件的Name属性,分别命名为Cut,Copy,Paste,Clear。您会发现,当Name属性发生改变时,C aption属性将发生相应的变化。在Caption属性前加标“&”号设立加速键

将memo部件的ScrollBars属性设为ScVertical,以便加上滚行条。将WordWrap 属性设置为True,这样当用户输入文本到达Memo部件的右边缘时会自动回行。将Line属性第一行的Memo1文本删除,使得memo部件在初始显示时为空的。

为每一个按钮建立如下的事件处理过程:

procedure TForm1.CutClick(Sender: TObject);

begin

Memo1.CutToClipboard;

end;

procedure TForm1.CopyClick(Sender: TObject);

begin

Memo1.CopyToClipboard;

end;

procedure TForm1.PasteClick(Sender: TObject);

begin

Memo1.PasteFromClipboard;

end;

procedure TForm1.ClearClick(Sender: TObject);

begin

Memo1.clear;

end;

执行此程序。您可以在备注部件中输入文本,在进行了文本的标记后,可以任意地进行剪切、拷贝、粘贴和清除。当按钮被按动时,就调用相应的过程进行处理。用户可以通过查阅在线帮助进行Memo部件的Topic Search,在Memo Component项中查阅Method,会得到以上过程的详细说明。

2.1.

3.2 调用Delphi的含参过程

有些过程要求用户指明参数。被调用的过程会在执行时使用传入的参数值,这些值在过程中被认为是已经被说明的变量。例如,LoadFromFile方法在TString对象中被说明为:

Procedure LoadFromFile(const FileName: String);

在调用这一过程时,应指明FileName参数是要装入的文件名称。下面的程序将先打开Open对话框,当您选择了一个文件后,Delphi将把该文件读入一个Memo部件:

begin

OpenDialog.Execute;

Memo1.lines.LoadFromFile(OpenDialog.FileName);

end;

2.1.

3.3 使用Delphi函数

与过程一样,函数的程序代码也执行特定的工作。它和过程的差别为:函数执行时会返回一个值,而过程则没有返回值。函数可以用来赋给一个属性或变量;也可以使用返回值来决定程序的流程。

前文中我们实际上已经接触过了函数。在讲述变量时,曾用到过下面的程序段:Edit1.Text := IntToStr(X + Y);其中,IntToStr(Value)把一个LongInt类型的数值转化

为字符串的值,Value是IntToStr唯一的参数,它可以是一个整形的值、变量、属性或产生整形值的表达式。调用函数,必须把返回值赋给和此返回值类型兼容的变量或属性。

有些函数返回一个True或False的布尔量,用户的程序可以根据返回值来决定跳转。下文的例程讲述了函数返回值为Boolean的判断用法:

在窗体中加入一个ColorDialog对象和一个Name属性为ChangeColor的按钮。为按钮的On Click事件建立事件处理过程如下:

procedure TForm1.ChangeColorClick(Sender: TObject);

begin

if ColorDialog1.Execute then

Form1.Color := ColorDialog1.Color

else

Form1.Color := clRed;

end;

此事件处理过程使用一个返回Boolean值的Execute方法。按动按钮,并在颜色对话框中选择一个颜色。如果按动OK按钮,ColorDialog.Execute方法将返回True,则Form 1.Color将被赋值为ColorDialog1.Color,窗体显现您选用的颜色;如果按动颜色对话框的Cancel按钮,方法将返回False值,窗体将变为红色。

2.1.4 跳转语句

Object Pascal的跳转语句有if和case两个。

2.1.4.1 if语句

if语句会计算一个表达式,并根据计算结果决定程序流程。在上文的例程中,根据ColorDialog.Execute的返回值,决定窗体的背景颜色。if保留字后跟随一个生成Bool ean值True或False的表达式。一般用“=”作为关系运算符,比较产生一个布尔型值。当表达式为True时,执行then后的语句。否则执行else后的代码,if语句也可以不含els e部分,表达式为False时自动跳到下一行程序。

if语句可以嵌套,当使用复合语句表达时,复合语句前后需加上begin…end。el se保留字前不能加“;”,而且,编译器会将else语句视为属于最靠近的if语句。必要时,须使用begin…end保留字来强迫else部分属于某一级的if语句。

2.1.4.2 case语句

case语句适用于被判断的变量或属性是整形、字符型、枚举型或子界型时(LongI nt除外)。用case语句进行逻辑跳转比编写复杂的if语句容易阅读,而且程序代码整形较快。

下面的例程显示一个使用case语句的窗体:

建立如下的事件处理过程:

procedure TForm1.Button1Click(Sender: TObject);

var

Number : Integer;

begin

Number := StrToInt(Edit1.Text);

case Number of

1,3,5,7,9: Label2.Caption := '奇数';

0,2,4,6,8: Label2.Caption := '偶数';

10..100:

begin

Label2.Caption := '在10到100之间';

Form1.Color := clBlue;

end;

else

Label2.Caption := '大于100或为负数';

end;

end;

执行程序,当Edit1部件接受到一个值,并按动“OK”按钮触发程序后,Number 便被赋值为用户输入的数值。case语句根据Number的值判断该执行哪一条语句。象if语句一样。case语句也有可选择的else部分。case语句以end结尾。

2.1.5 循环语句

Object Pascal的循环语句有三种:repeat、while和for语句。

2.1.5.1 repeat语句

repeat语句会重复执行一行或一段语句直到某一状态为真。语句以repeat开始,以until结束,其后跟随被判断的布尔表达式。参阅以下的例程:

i := 0;

repeat

i := i+1;

Writen(i);

until i=10;

当此语句被执行时,窗体的下方会出现1到10的数字。布尔表达式 i=10 (注意,与其他语言不同的是,“=”是关系运算符,而不能进行赋值操作)直到repeat..until程序段的结尾才会被计算,这意味着repeat语句至少会被执行一次。

2.1.5.2 while语句

while语句和repeat语句的不同之处是,它的布尔表达式在循环的开头进行判断。while保留字后面必须跟一个布尔表达式。如果该表达式的结果为真,循环被执行,否则会退出循环,执行while语句后面的程序。

下面的例程达到和上面的repeat例程达到同样的效果:

i := 0;

while i<10 do

begin

i := i+1;

writeln(i);

end;

2.1.5.3 for语句

for语句的程序代码会执行一定的次数。它需要一个循环变量来控制循环次数。您需要说明一个变量,它的类型可以是整形、布尔型、字符型、枚举型或子界型。

下面的程序段会显示1到5的数字,i为控制变量:

var

i : integer;

for i := 1 to 5 do

writeln(i);

以上介绍了三种循环语句。如果您知道循环要执行多少次的话,可以使用for

语句。for循环执行速度快,效率比较高。如果您不知道循环要执行多少次,但至少会执行一次的话,选用repeat..until语句比较合适;当您认为程序可能一次都不执行的话,最好选用while..do语句。

2.1.6 程序模块

程序模块在Object Pascal中是很重要的概念。它们提供了应用程序的结构,决定了变量、属性值的范围及程序执行的过程。它由两个部分组成:可选择的说明部分和语句部分。如果有说明部分,则必在语句部分之前。说明部分包括变量说明、常量说明、类型说明、标号说明、程序,函数,方法的说明等。语句部分叙述了可执行的逻辑行动。

在Delphi中,最常见的程序模块便是事件处理过程中的程序模块。下面的事件处理过程是含有变量说明部分的程序模块:

procedure TForm.Button1Click(Sender Tobject);

var {程序模块的说明部分}

Name : string;

begin {程序模块的语句部分}

Name := Edit1.Text;

Edit2.Text := 'Welcome to Delphi'+Name;

end; {程序模块结束}

库单元也是程序模块。库单元的interface部分含有库函数、类型、私有,公有域的说明,也可以含有常量、变量的说明。这一部分可以作为程序模块的说明部分。在库单元的implementation部分中通常含有各种事件处理过程,它们可以视为模块的语句部分,是事件处理模块。库单元模块结束于库单元结束的end.处。

程序模块中可以包含其他的程序模块。上文库单元模块中含有事件处理模块。而库单元模块实际是在工程程序模块中。

所有的Delphi应用程序都有相同的基本结构。当程序逐渐复杂时,在程序中加入模块即可。例如在库单元模块中加入事件处理模块,向工程中加入库单元模块等。模块化编程使得程序结构良好,并且对数据具有保护作用。

2.1.7 关于作用范围

2.1.7.1 标识符的作用范围

一个变量、常量、方法、类型或其他标识符的范围定义了这个标识符的活动区域。对于说明这个标识符的最小程序模块而言,此标识符是局部的。当您的应用程序在说明一个标识符的程序模块外执行时,该标识符就不在此范围内。这意味着此时执行的程序无法访问这个标识符,只有当程序再度进入说明这个标识符的程序模块时,才可以访问它。

下面的示意图表示一个含有两个库单元的工程,每个库单元中又各有三个过程或事件处理过程。

2.1.7.2 访问其他程序模块中的说明

您可以在当前的程序模块中访问其他程序模块中的说明。例如您在库单元中编写一个事件处理过程来计算利率,则其他的库单元可以访问这个事件处理过程。要访问不在当前库单元中的说明,应在这个说明之前加上其他应用程序的名称和一个点号(.)。例如,在库单元Unit1中有事件处理过程CalculateInterest过程,现在您想在库单元Unit2中调用这一过程,则可以在Unit2的uses子句中加入Unit1,并使用下面的说明:

Unit1.CalculateInterest(PrincipalInterestRate : Double);

应用程序的代码不能在一个模块外访问它说明的变量。事实上,当程序执行跳出一个模块后,这些变量就不存在于内存中了。这一点对于任何标识符都是一样的,不管事件处理过程、过程、函数还是方法,都具有这一性质。这样的标识符称为局部变量。

2.1.7.3 按照作用范围说明标识符

您可以在应用程序的不同地方说明一个标识符,而只需保证它们的有效范围不同即可。编译器会自动访问最靠近当前范围的标识符。

库单元的全局变量一般可以说明在保留字implementation后面。例如,下面的例程实现将两个编辑框中的整数相加,显示在第三个编辑框中。用到了一个整形的全局变量Cou nt:

…implememntation

var

Count : Integer;

procedure TForm1.AddClick(Sender:TObject);

var

FirstNumber,SecondNumber:Integer;

begin

Count := Count + 1;

Counter.Text := IntToStr(Count);

FirstNumber := StrToInt(Edit1.Text);

SecondNumber := StrToInt(Edit2.Text);

Edit3.Text := IntToStr(FirstNumber+SecondNumber);

end;

为了实现每按动一次按钮Count增加一次,必须对全程变量Count进行初始化处理。在程序库单元的结尾处,最后一个end.保留字之前,加入保留字initialization和初始化Count的代码:

initialization

Count := 0;

这样当事件处理过程AddClick被触发时,Count就会被增加一次,以表征计算次数。如果用面向对象编程,则Count可以说明成窗体的一个域,这在下一节中将有讲述。

2.1.8 编写一个过程或函数

在您开发Delphi应用程序时,所需的大部分代码都编写在事件处理过程中,但有时仍然需要编写不是事件处理过程的函数或过程。例如,您可以把在多个事件处理过程中用得到语句编写成过程,然后任何事件处理过程、过程、函数都可以象调用已经存在的过程或函数一样直接调用它。好处是您只需编写一次代码,而且程序代码会比较清楚。

2.1.8.1 一个自行编写的函数例程

在上文两个数相加的程序中,如果编辑框中无值,则会使得程序出错中断。为避免这种情况,编写下面的函数,检查编辑框中是否有值,如无值,则提醒用户输入:

function NoValue(AnEditBox:TEdit):Boolean;

begin

if AnEditBox.Text='' then

begin

AnEditBox.Color := clRed;

AnEditBox.Text := '请输入整数值';

Result := True;

end

else

begin

AnEditBox.Color := clWindow;

Result := False;

end;

end;

NoValue函数会检查编辑框是否为空,如果是,编辑框颜色变红,并提醒用户输入一个整数,然后函数返回真值;Result保留字在Delphi中用来专指函数返回值。在上文的例程中加入NoValue函数:

procedure TForm1.AddClick(Sender: TObject);

var

FirstNumber,SecondNumber : Integer;

begin

if NoValue(Edit1)or NoValue(Edit2) then

exit;

Count := Count + 1;

Counter.Text := IntToStr(Count);

FirstNumber := StrToInt(Edit1.Text);

SecondNumber := StrToInt(Edit2.Text);

Edit3.Text := IntToStr(FirstNumber+SecondNumber);

end;

如果其中的任何一个返回真值,则表示有编辑框空,会执行exit过程,使得当前的程序模块停止执行,并使得编辑框出现输值提示。当新值被输入后,再执行程序时,红色提示被隐去,恢复正常的计算状态。

2.1.8.2 过程和函数的标题

每一个过程或函数都以标题开始,其中包括过程或函数的名称和它使用的参数。过程以保留字procedure开始,函数以保留字function开始。参数位于括号里面,每一个参数以分号分隔。例如:

procedure validateDate(Day : Integer; month : Integer; Year : Integer);

您也可以将相同类型的参数组合在一起,则上述过程头写作:

procedure ValidateDate(Day, Month, Year : Integer);

函数在标题中还多了一项:返回值的类型。下面是一个返回值为Double型的函数标题:

function CalculateInterest(principal,InterestRate:Double):Double;

2.1.8.3 函数和过程中的类型说明

一个过程或函数程序模块也含有说明部分和语句部分。说明部分可以包括类型说明、变量说明、常量说明等。除了Object Pascal语言中已经定义的类型之外,Delphi的应用

程序还可以建立新的数据类型。类型说明部分有保留字type开始。下面是一些类型的说明:type

Tcount = Integer;

TPrimaryColor = (Red,Yellow,Blue);

TTestIndex = 1..100;

TTextValue = -99..99;

TTestList = array [TTestIndex] of TTestValue;

TCharVal = Ord('A')..Ord('Z') ;

Today = (Monday,Tuesday,Wednesday,Thursday,Friday,Saturday,

Sunday) ;

在类型标识符后面,用“=”号定义了新的类型。类型界定了变量的取值范围,例如,TCount类型的变量必须是整形值;一个TPrimaryColor类型的变量只能是red、yel low或blue等等。每一个类型的名称都是由字母T开始,这并非必须的,但它是Delphi的惯例,在区别类型名和标识符时非常有用。类型说明可以是局部的,也可以是全局的。如果您把它放在implementation后面,则表明对于库单元来讲,它是全局的,所有的事件处理过程和其他的过程、函数都可以调用它。如果类型是在过程中被说明的,则是局部的,离开这一过程,该类型将失效。

一般来讲,在过程和函数中,任何类型说明都在变量说明之前,而任何变量说明都在常量之前。但是,只要遵从说明必须在过程与函数的标题之后,而且在程序代码之前,即是有效的。

2.1.8.4 过程和函数的语句部分

过程或函数的语句部分由begin开始,end结束。函数需要一个返回值。可以将返回值赋给函数名称,也可以将返回值赋给Result变量。下面的例程将返回值赋给函数名称:

function CalculateInterest(Principal,InterestRate: Double):Double;

begin

CalculateInterest := Principal * InterestRate;

end;

将返回值赋给Result变量也是可以的,则上面的程序改为:

Result := Principal*InterestRate;

下面是这个函数的调用方法:

InterestEarned :=CalculateInterest(2000,0.012);

在Implementation后面的过程和函数,可以且只能被此库单元的事件处理过程使用。要让过程和函数可以被其他的程序库单元使用,则需要将过程或函数的标题部分放在库单元中的interface部分,而把含标题的整个过程或函数放在库单元的inplementation部分,并在要访问这个过程或函数的库单元的uses子句中加入说明这个过程或函数的库单元名称。

2.1.8.5 函数的递归调用

在Object Pascal中,过程或函数必须先说明再调用。上文的NoValue函数必须在使用它的事件处理过程之前说明和执行,否则程序会报告一个未知标识符的错误。

以上规则在递归调用时是例外情况。所谓递归调用,是指函数A调用函数B,而函数B又调用函数A的情况。在递归调用中,函数要进行前置,即在函数或过程的标题部分最后加上保留字forword。下文的例程是一个递归调用的典型例子:

implementation

var

alpha:Integer;

procedure Test2(var A:Integer):forword;

{Test2被说明为前置过程}

procedure Test1(var A:Integer);

begin

A :=A-1;

if A>0 then

test2(A); {经前置说明,调用未执行的过程Test2}

writeln(A);

end;

procedure Test2(var A:Integer);{经前置说明的Test2的执行部分}

begin

A :=A div 2;

if A>0 rhen

test1(A); {在Test2中调用已执行的过程Test1}

end;

procedure TForm1.Button1Click(Sender:TObject);

begin

Alpha := 15; {给Alpha赋初值}

Test1(Alpha); { 第一次调用Test1,递归开始}

end;

按钮的OnClick事件处理过程给Alpha赋初值,并实现先减1再除2的循环递归调用,直到Alpha小于0为止。

2.1.8.6 过程和函数的参数

当您的程序代码在调用一个过程或函数时,通常用参数传递数据到被调用的过程或函数中。最常用的参数有数值参数、变量参数和常量参数三种。

由被调用过程或函数定义的参数为形参,而由调用过程或函数指明的参数叫实参。在NoValue函数中,说明函数体中的AnEditBox是形参,而调用时在if NoValue(Edit1)…中,Edit1是实参。

数值参数在运行过程中只改变其形参的值,不改变其实参的值,即参数的值不能传递到过程的外面。试看下面的例程:

procedure Calculate(CalNo:Integer);

begin

CalNo := CalNo*10;

end;

用以下例程调用Calculate函数:

Number := StrToInt(Edit1.Text);

Calculate(Number);

Edit2.Text := IntToStr(Number);

Number接受由编辑框1输入的数值,经Calculate过程运算。它是一个数值型实参。在进入Calculate函数后,会把Number实参拷贝给形参CalNo,在过程中CalNo增大十倍,但并未传递出来,因此Number值并未改变,在编辑框2中显示仍然是编辑框1中的输入值。形参和实参占用不同的内存地址,在过程或函数被调用时,将实参的值复制到形参占用的内存中。因此出了过程或函数后,形参和实参的数值是不同的,但实参的值并不发生变化。

如果您想改变传入的参数值,就需要使用变量参数,即在被调用程序的参数表中的形参前加上保留字var。例如:

procedure Calculate(var CalNo : Integer);

则CalNo并不在内存中占据一个位置,而是指向实参Number。当一个变参被传递时,任何对形参所作的改变会反映到实参中。这是因为两个参数指向同一个地址。将上一个例程中过程头的形参CalNo前面加上var,再以同样的程序调用它,则在第二个编辑框中会显示计算的结果,把第一个编辑框中的数值放大十倍。这时形参CalNo和实参Number的值都是Nnmber初始值的10倍。

如果当过程或函数执行是要求不改变形参的值,最保险的办法是使用常量参数。在参数表的参数名称前加上保留字const可以使一个形参成为常量参数。使用常量参数代替数值参数可以保护您的参数,使您在不想改变参数值时不会意外地将新的值赋给这个参数。

2.1.9 定义新的数据类型

Object Pascal有一些系统预定义的数据类型,在2.1.2中已经对它们作了介绍。您可以利用这些数据类型以建立新的数据类型来满足程序的特定需要。下面简单地叙述了您能建立的主要数据类型,如枚举型、子界型、数组型、集合型、记录型、对象型等。

2.1.9.1 枚举类型

一个枚举型的说明列出了所有这种类型可以包括的值:

type

Tdays=( Sunday ,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday);

可以定义上述枚举类型的变量:

var

DayOfWeek:TDays;

在枚举型中,括号中的每一个值都有一个由说明它的位置决定的整形值。例如Sun day有整形值0,Monday有整形值1等。您可以把DayOfWeek说明为一个整形变量,并将一星期的每一天赋一个整形值以达到相同的效果,但用枚举型会使得程序可读性好,编写容易。当您在枚举型中列出值时,您同时说明了这个值是一个标识符。例如您的程序中如果已经含有TDays类型且说明了DayOfWeeks变量,则程序中便不能使用Monday变量,因为它已经被说明为标识符了。

2.1.9.2 子界类型

子界型是下列这些类型中某范围内的值:整形、布尔量、字符型或枚举型。在您想限制一个变量的取值范围时,子界型是非常有用的。

type

Thours = 0..23;

TValidLetter = 'A' .. 'F';

TDays = ( Sunday ,Monday,Tuesday,Wednesday,Thursday,

Friday,Saturday); {枚举型}

TWorkDay = Monday..Friday; {一个TDays型的子界}

子界型限定了变量的可能取值范围。当范围检查打开时,(在库单元的Implementa tion后面有{$R*.DFM}字样表示范围检查打开,否则您可以在Options|Project|Complier Options中选择Range Cheking来打开范围检查),如果变量取到子界以外的值,会出现一个范围检查错误。

2.1.9.3 数组类型

数组是某种数据类型的有序组合,其中每一个元素的值由其相对位置来指定,您可以在数组的某个位置上放置数据,并在需要时使用这些数据。下面的类型说明了一个Doubl e型的数组变量:

var

Check : array [1..10] of Double;

它表示Check指向一个含有10个Double型元素的数据串列,代表每一个元素的是1到10之间的数字,称为索引。数组的每一项由数组名称加上[]中的索引来表示。Check 包含10个变量,Check[1]表示第一个变量。您也可以把数组定义成类型:

type

TCheck = array[1..10] of Double;

则变量说明改为:

var

Check :TCheck;

您可以通过给数组赋值等方法来使用数组。下面的语句将0.0赋给Check数组中的所有元素:

for J := 1 to 10 do

Check[J] := 0.0;

数组也可以是多维的,下面的类型定义了一个20行、20列的数组。

type

Ttable = array[1..20,1..20] of Double;

var

table1:TTable;

想将这一表格的所有数据初始化为0.0,您可以使用for循环:

var

Col,Row:Integer;

相关主题
相关文档
最新文档