走进Delphi

走进Delphi

本章回答了谁是本书的读者。本书适用于中、高级的Delphi程序员,以及从Visual BasicC++语言迁移到Delphi的专业程序员。

对于中等程度或过渡型的读者,本章提供了调整的机会。本章是专门为中等程度及过渡型的读者所设计的。如果您对基础知识了解得很清楚,可以跳过本章。

本章试图保证在涉及基础知识时,我们大家都处于同一水准上。对于中等技术水平的Delphi程序员,这一章将填补他的一些知识漏洞;而对于过渡型的读者,这一章则架起了通往本书及Delphi的其余部分的桥梁。

Delphi之美在于它令人迷惑般的简单。我曾使用CC++JavaVisual BasicAccess及其他许多语言进行编程,但Delphi一直是我最喜爱的语言。从表面上看,DelphiVisual Basic有惊人的相似。

作为一种工具,Delphi建立在成熟的面向对象语言Object Pascal之上。使用Delphi,您可以做到能用C++完成的任何事情但避免C++的大部分麻烦;使用Delphi,您可以创建Visual Basic应用程序。反过来则是做不到的。在C++中,程序员可能会在内存管理、模板、操作符重载时遇到麻烦,而在Delphi中则不会出现这种问题。在Visual Basic中您会很快遇到一些障碍,而且会经常出现这样的情况。Delphi既具有C++的强大性又具有Visual Basic的易用性。

1.1  浏览和配置IDE

当您运行Delphi时就可以看到IDE(集成开发环境,Integrated Development Environment)。事实上,Delphi的先驱之一Turbo Pascal就提供了业界最早的IDE。在早期版本的Turbo Pascal中,集成开发环境用于建立DOS应用程序,在当时非常有效,就像现在的Delphi一样。我们来回顾一下Delphi的基本功能。

1.1.1  打开文件和保存文件

Delphi存在的目的是用于Windows编程。而Windows程序具有图形用户界面。Delphi工程的图形部分存储在描述程序外观的资源文件中,代码则写在另一个文件中。因此每个Windows工程都会有两个或更多的文件。文件的集合称之为工程。当您要编写程序时,您需要创建一个新的工程。

创建一个新的工程

很幸运,当您打开Delphi时,会默认地创建一个新的Windows应用程序工程。也可以运行Delphi来创建新的工程,按Alt + FN键即可。创建的新工程的选项位于FileNew菜单中。这两种途径都可以得到新的工程,如图1.1所示。

1.1  创建一个新工程时桌面的外观

显示在前台的单元文件可供编写程序代码。显示在后台并与单元文件稍有偏移的是窗体,可在其中设计程序的图形用户界面。根据需要,Delphi创建的Windows应用程序可拥有任意数目的窗体,每个窗体有一个单元文件和窗体文件。单元的数目同样的不受限制,但并非所有的单元都有窗体。如果单元不需要可视化的外观,那么它就不需要窗体。按Alt+FNU键可以创建无窗体的单元。这就是从FileNew菜单中创建一个新单元的过程。

打开文件和关闭文件

为管理桌面需要打开或关闭文件,这取决于您的工作在程序代码中所处的位置。文件操作可以在File菜单中找到。要关闭特定的文件,请确认代表该文件的窗体或单元位于前台。按Alt+FC键可以关闭前台文件。当您所关闭的单元有相关联的窗体时,关联的窗体也会被关闭,反之亦然。

打开文件是在File菜单中的操作。可以按Alt+FO键来打开文件,或使用File菜单中的Reopen命令(刚好位于Open菜单项之下)来选择一个最近关闭的文件。当打开的单元有相关联的窗体时,只需选择单元的名字则窗体文件也会打开。

保存文件

同所有的文件操作一样,Save操作也在File菜单中。您可以选择SaveSave AsSave Project As,或Save All等菜单项来保存您的工作。Save用于保存前台文件,Save As用新的名字保存前台文件,Save Project As以不同的名字保存工程,而Save All则用一次操作保存了工程中的所有文件。当保存工程时,您对工程文件的命名再加上.DPR扩展名将成为编译时程序的名字。

1.1.2  搜索代码

程序员通常会在几个星期之内忘掉所编写的代码。程序员在跟踪并修改代码时,可能会花费相当可观的时间查找代码。Delphi提供了一些很好的工具来做这些工作。

查找与替换

DelphiSearch菜单提供了几种搜索方法。在Search菜单中,能执行FindFind in Files, ReplaceSearch Again或者Incremental Search等操作。例如,当您按Ctrl+F键时,就会显示如图1.2所示的Find对话框,它包含两个属性页。按Ctrl+R键则显示Replace Text对话框,它比Find对话框多一个输入域,您可以在其中输入文字用以替代找到的文字,除此之外它与Find对话框几乎完全相同。

1.2   Ctrl+F键显示如图的带两个属性页的Find对话框。Find

    属性页可用于在前台文件中查找,Find in Files属性页

       是基于文件路径和掩码,可在一个或多个文件中进行查找

F3键可运行Search Again菜单项。这将利用上一次输入到FindFind in FilesReplace对话框中的信息重复上一次查找。一个不错的附加查找功能是Incremental Search(增量查找)。按Ctrl+E键可启动增量查找。如果发现匹配,增量查找将寻找键入的每个字符。在图1.3中键入字母me,结果关键字implementation中的me被找到,从而被高亮显示。

跳到行号

如果知道要检查的代码的行号,Go To Line Number功能是很有用的。按Alt+G键就可以使用Go To Line Number功能。例如,如果把代码连同行号打印出来并发现了一个错误,这个命令很快就能找到错误位置。另一项技术是创建日志文件,当代码运行时向每个日志项添加行号,然后可以用日志文件中引用的行号和Go To Line Number功能迅速把光标移动到该行代码。

1.3  Ctrl+E键启动增量查找;查找过程会像您分别键入了每个字符一样地执行

浏览对象

如图1.4所示是Browse Symbol对话框,它可以迅速查找关于一个类的所有信息,或者对象类的某个实例的所有引用。当您按Alt+SY键并输入TForm(一个类名)将显示如图1.5所示的对话框,其中有ScopeInheritance属性页,并包含了所有引用了TForm类的单元。在本例中可以看到forms.pasUnit1.pas,其中forms.pas包含了TForm类的实现,而Unit1.pas中包含了一个对TForm的引用。

1.4  Alt+SY键打开如图1.5所示的Symbol explorer

它提供关于输入的符号属性的详细信息

1.5  输入变量名,您只能得到引用该变量的单元的名字

如果您键入对象的名字,Symbol Explorer将只显示一个列表,其中包含了引用该对象的单元的名字。

1.1.3  探索Delphi

Delphi是一种基于Object Pascal的快速应用程序开发工具,Object Pascal是全功能的面向对象语言,这意味着:其功能主要集中在类的定义方面,这些类映射到描述问题域行为和数据的实体。而属性可以是数据或过程。

随之而来,在Delphi中必须有管理对象信息的工具。有几种这样的工具。很明显的一种是代码。对于过渡型的程序员,不那么清楚的一对工具是Object InspectorObject TreeView。本节将示范如何使用Object Inspector Object TreeView,以便在设计时管理对象。

Delphi还有其他一些有趣的特性,可有助于管理任务列表、调试任务,以及窗体的可视化布局。除了Object InspectorObject TreeView之外,本节还将示范使用To-Do List、特定的调试视图和Alignment Palette

Object Inspector

如图1.6所示的Object Inspector是一个数据访问对话框,可用于在设计时定义对象数据的默认值。如图所示,当前对象和类分别是Form1TForm1。如果对列出的值进行修改,就改变了Form1的初始值。

1.6  在设计时从Object Inspector顶部的下拉

     列表中选定对象,即可改变其初始值

Properties属性页中,包含了设计时Form1对象所有可改变的数据。Events属性页则包含了对Windows消息作出反应的一些特别的特性。事件基本上是对鼠标单击之类的Windows消息作出响应的子程序。单击Event属性页,并在右栏中双击,Delphi会创建Shell函数,响应与该事件相关的消息。例如,单击Event属性页,在与OnClick事件相邻的右栏中双击,Delphi会创建如图1.7所示的空过程。

1.7  双击一个事件特性将在单元中创建一个空过程,该空过程包含了双击的

       事件特性所对应对象的引用。在图中Form1对象的OnClick事件被双击

通过向该过程添加代码,您可以对该事件作出特定的响应。在例子中,单击窗体将运行OnClick事件相应的代码。请动手试一下这个例子。

1. 创建一个新的工程。

2. 如果Object Inspector没有打开,请按F11键打开它。

3. Object Inspector中,如果Form1没有被选定,请从下拉列表框中选定它。

4. 单击Events属性页。

5. 找到OnCreate事件(请注意事件是按照字母顺序排列的)。

6. OnCreate事件的右侧双击。

7. 单元中添加窗体的OnCreate事件的Shell过程。将下面一行代码添加到beginend之间的光标闪烁处。

ShowMessage('Hello World!');

8. F9键运行程序。将显示如图1.8所示的对话框。

1.8  前面的列表中所描述的例子程序的输出

注意:如果偶尔对某个事件创建了事件过程,而又并不真正需要它,只要不向begin和end关键字之间添加任何代码即可;下次保存工程或该文件时,Delphi将清除空的事件过程。

虽然可以手工编码事件过程,但上面的方法是向对象添加事件过程的最简单的途径。当向非可视化控件添加事件过程时,您需要创建事件过程并将其与事件的数据和代码联系起来。第6章将示范该技术。

最后要注意:Object Inspector不允许对特性或事件赋予错误类型的初值。在Object Inspector中,向数据赋值的代码与运行时所用的代码是相同的,因此其行为也是类似的。一般来说,如果对象的属性的赋值不合适,将会显示如图1.9所示的异常。无论在设计时或运行时出现无效赋值,都会发生异常。

1.9  如果向对象的属性输入无效值,Object Inspector使用异常处理进行恢复

Object Treeview

Delphi是面向对象的开发环境,因此用户可以从管理对象间相互关系的机制中受益。一种机制便是Object Treeview。按Alt+Vj键可以打开Object TreeviewObject Treeview以层次化的方式在对象中进行定位。Object Inspector只是按字母顺序列出所有的对象,而Object Treeview则按照对象之间关系的顺序将其列出。

例如当一个OK按钮位于窗体之上时,从语义上可称窗体拥有该按钮。当在Object Treeview中单击某对象时,则该对象将成为Object Inspector中的当前对象。以这种方式,Object Treeview可帮助您很快的找到并管理特定对象的属性。

To-Do List

Delphi 5中添加了To-Do List特性。To-Do ListPost-It® 记事本功能相仿。当处于任务当中或结束当天工作时,如果需要保存退出时的信息,可使用To-Do List在工作进度上附加注记。

To-Do List是可定制的。要添加To-Do 项,可以右击窗体或单元,并单击Add To-Do Item菜单即可。图1.10显示了Add To-Do Item对话框。键入文本(Text)提示信息,添加有意义的优先(Priority)值,输入或从下拉列表框中选择所有者(Owner)的名字,并输入类别(Category)。由于To-Do List是可定制的,因此通过试验,您可以根据自己的需要添加或删除一些栏,对提示信息进行优化。

1.10  1章的To-Do提示信息

Alt+VL键,即可看到To-Do List。双击任一特定的To-Do项,代码编辑器将把光标定位到该To-Do项输入时的代码位置。单击To-Do项旁边的复选框,将会更新代码中的文字注释,表示该To-Do项已完成。

调试视图

有许多种调试视图,在调试应用程序时它们可提供大量的细节信息。从图1.11可看出,调试时可访问断点、调用栈、变量查看(Watches)、局部变量、线程、模块、事件日志、CPUFPU等。

1.11  使用ViewDebug Windows对话框,从各

     种不同的角度获取应用程序的特定信息

断点(Breakpoints)表示代码中的停止点。当调试器遇到断点时,将在该位置停止执行。调用栈(Call stack)按照调用的逆序显示所有处于活动中的过程,单击调用栈列表中的任一项均可回溯到对应的代码。双击调用栈中的某一项,代码编辑器将定位到执行发生转移的那一行代码。双击调用栈对话框中的特定过程名,即可转到对应的特定过程。变量查看(Watches)窗口中有一些变量,当程序以调试模式运行时,可观察这些变量的值。您既可以观察简单的数据变量,也可以观察对象变量。局部变量(Local Variables)对话框中显示了对应于当前过程的变量的名字和值。

线程(Threads)视图中可以看到应用程序中所有线程的状态。只有存在额外的线程实例,才能够使用该视图。模块(Modules)视图可检查所有的动态链接库以及API过程的入口点,还能定位组成应用程序的Delphi模块的源代码。按Alt+VDE键可显示事件日志(Event Log)视图,它可以提供与Windows NT事件日志类似的有用信息。

注意:Delphi中,通过继承定义在classes单元中的TThread类,可以相对容易地创建多线程应用程序。

最后,CPUFPU视图显示了中央处理单元和数学协处理单元的状态,包括汇编语言指令、寄存器和标志状态,以及内存中的原始数据。使用本节提到的调试视图,通过一番练习,您很快就可以回溯到代码并找到错误的原因。

Alignment Palette的使用

Alignment PaletteView菜单中的一项(见图1.12)。它设计用来沿着某一方向的边界,对所有的可视化控件进行对齐。假定有如图1.13所示的标签和编辑域。按下列步骤可对二者按其垂直中心进行对齐:

1.12  使用Alignment Palette能够可视化地组织控件,直至合适为止

1.13  要选定多个控件,单击并拖动出一个矩形包围所有的控件即可

1在最左上方控件的左上方选择一点进行单击,拖动鼠标至最右下方控件的右下方(参照图1.13)。这样即可同时选择多个控件

2.在选择了需要对齐的所有控件之后,按Alt+VA键打开Alignment Palette,如图1.12所示(如果漏掉了某个控件,可在按住Shift键的同时单独单击该组件,即可将其加入到已选定的组中)。

3.单击Alignment Palette中的Align Vertical Centers按钮(如图1.14所示),即可按照垂直方向的中心在水平方向对齐Label1Edit1控件。

1.14  Alignment PaletteAlign Vertical Centers按钮将把控件按同一

       水平轴线对齐,这对于对齐如图的labeledit域是很合适的

关于控件的对齐,大体上就这些知识。当您打算完善图形用户界面时,通过使用Alignment Palette提供的各种对齐方式,很快就可以安排好控件的位置。

1.1.4  运行程序

Delphi的集成开发环境使得以步进方式运行应用程序非常容易,从而简化了开发者的单元级测试。

运行您的应用程序

F9键可运行应用程序。在运行模式下,只有设置了断点或者从Run菜单中选择了Program Pause,才会使程序停下来并交出控制权。断点可用F5键设置或取消。如果您需要步进运行程序,可按F8键或Alt+RS键。步进会按步执行每条语句,但并不跟踪到过程或函数的内部。如果您要进入过程内部,请按F7键或使用集成调试器的RunTrace Into特性。

Run菜单上,还有其他几个有用的集成调试菜单项,包括Trace to Next Source LineRun to CursorRun Until Return,以及Show Execution Point。如果已经进入到Windows API内部,并且希望调试器在返回到您的源代码(即可读的代码,而不是汇编码)时暂停,在这种情况下Trace to Next Source Line很有用。Run to Cursor则在光标所在之处隐含地设置了一个软件断点。Run Until Return则运行当前程序直到从当前过程中返回。Run菜单中的Show Execution Point菜单项可定位下一行将执行的代码。当您在阅读帮助文件信息时迷失了方向,或者步进执行程序时由于其他代码使得当前执行点在屏幕上不可见时,该特性可能会很有用处。

使用Code Watch

Run菜单也包含另外几个菜单项,可以观察整数这样的简单数据和对象这样的复杂数据的值。RunInspect菜单项可以打开Inspect对话框,如图1.15所示。输入对象的名字,例如Application对象,即可在Debug Inspector窗口中得到该对象的详细视图(如图1.16所示)。Debug Inspector包含了有关对象的数据、方法、属性的信息,所有这些信息对于程序的正确性和健壮性都是必需的。

1.15  Inspect对话框中输入变量或对象名,观察关于该对象状态的详细信息

Debug Inspector的实际外观是可以改变的,它随着所观察数据的种类而变化。如果观察整数这样的简单类型,只能看到Data属性页。从图1.16可看出,如果观察对象,将看到图中所示的三个属性页。

1.16  Debug Inspector包含了详细的数据、方法、特性

信息。图中显示了Application对象的特性的信息

Debug Inspector可以修改数据的值、显示完整的名字、观察嵌套的值和对被观察的值进行类型转换等。观察嵌套的值可以深入到对象内部,观察其底层的值。在Delphi中,当对象声明为超类,但其实例的实际值是子类时,对观察到的值进行类型转换是很有用的。

举与之相类似的例子,汽车代理商可能有许多汽车;而当您买汽车时,您说的是特定的厂家和型号术语。这样,汽车代理商可以把汽车当作超类,而把Jeep Grand Cherokee作为一种汽车,即一个子类。对特定的讨论来说,您所需的细节的层次依赖于您把代理看作是汽车的集合还是特定的汽车。请思考:

“您从事何种工作?”

“我卖汽车。”

在上面的对话中,一般的汽车是可接受的。现在考虑下面的对话:

“您有2000 BMW M5 SUV吗?”

“没有。”

在第二段对话中,使用一种特定类型的汽车BMW,即一种子类将较为合适。在代码中模拟现实世界的概念时,进行不同层次的抽象是恰当的,因为在现实中这也是必要的。在数字世界中可能有汽车类型的变量,例如待售的汽车的列表,但列表中每个单独的元素都是一种特定类型的汽车。在这个例子中,Debug Inspector中的类型转换会比较有用。

计算并修改数据

Delphi的全部工具中,RunEvaluate Modify菜单项是最为有用的调试工具之一。Evaluate Modify菜单使得可以在运行中观察并改变一个值。举例来说,如果怀疑代码中的错误与某个变量的实际状态有关,您可以观察它并验证假定是否是正确的。无须停止调试过程和修改代码,只要在New Value域(见图1.17所示的Evaluate / Modify对话框)中输入新值,并单击Modify按钮即可。然后可继续运行代码。使用该技巧,可以轻松地验证对代码的修改是否可解决数据方面的问题。一旦发现某个数据范围可使程序正确运行,即可修改代码以使之全速运行。

1.17  Evaluate / Modify对话框用于运行时察看数据并立即

          做出修改。单击WatchInspect按钮来更深入的查看

添加Watch

可以从Run菜单添加Watch,从上一节提到的Evaluate / Modify对话框中进行添加亦可。添加Watch将打开Watch List对话框(见图1.18),当程序向前运行时,可以观察到数据值的改变。

1.18  使用Watch List对话框来观察数据值的改变

Ctrl+E键可显示Edit Watch对话框(见图1.19),即可编辑观察到的值。可以使该观察项有效或无效,或改变数据类型,而类型将决定数据的表示方式。一般来说,显示数据的默认表示方式是最合适的。

1.19  Edit Watch对话框用于编辑观察项的激活

       状态,或以另一种选择的方式表示数据

Run菜单的最后一项是Add Breakpoints子菜单。由于有许多信息与如何有效地使用断点有关,因此为断点单独增加了一个子菜单,更多的信息可以参考1.7节“调试程序”。在编写过一些代码之后,我们接触断点的机会就会更多一些了。

1.1.5  配置工作环境

Delphi对工作环境提供了广泛的控制能力。您可以修改编辑器和调试选项,亦可修改一般的Delphi环境选项。如果没有特别的原因,是无须改变环境设置的。在默认配置下,Delphi工作得很好。

当需要改变配置时,以下这些例子可能会比较有用:改变代码编辑的制表位距离,向已有的宏列表中添加版权标记,使Compiler Progress对话框有效,修改栅格大小(窗体上可见的那些点)使控件间的距离更加合适。当向窗体上放置控件时,栅格大小会影响控件默认放置的位置。

下面列出了一系列的步骤,按前面段落中提到的方式,演示了如何修改开发环境。

按下列步骤可改变制表位距离为两个空格:

1.按Alt+T键显示Tools菜单。

2.从Tools菜单选择Editor Options,将显示Editor Properties对话框。

3.在Editor Properties对话框的General属性页上,接近底部是Tab Stops域。输入您所喜欢的制表位距离,每两个制表位距离间插入一个空格,例如输入2 4 6 8 10后,当每次按Tab键时将移动两个空格。

按下列步骤可向Code Insight加入版权提示:

1.按前面例子步骤12,打开Editor Properties对话框。

2.单击Code Insight属性页。

3.在Code Template组中单击Add按钮。

4.在Add Code Template对话框中,向Shortcut Name域键入copyright、向Description域中键入My Copyright Stamp。单击OK按钮。

5.在Templates列表中,选定copyright项。

6.在Code Edit域中,键入需要使用的版权信息(例子见图1.20)。

1.20  Delphi提供的Code Insight工具可用于自动输入递归数据,

       或作为学习目的输入一些通用类型的短语,例如数组声明

提示:如果希望光标出现于Code Insight宏的某个位置,请在该位置键入|(管道符)。

要使用Code Insight,将光标置于代码模块中您希望Code Insight宏运行之处。例如,要把版权声明放在每个单元的顶部,先将光标置于单元顶部,按Ctrl+J键以显示Code Insight宏的列表,从列表中选择copyright,并且按Enter键(Code Insight宏列表请参见图1.21)。

1.21  Ctrl+J键显示Code Insight宏的列表

当编译或建立应用程序时,Compiler Progress对话框将显示编译的进度。执行下列步骤,可以启用Compiler Progress对话框:

1.按Alt+TE键,显示Environment Options对话框。

2.单击Preferences属性页(如果尚未选定该属性页)。

3.在Compiling and running组中,单击Show compiler progress。单击OK按钮。

现在当您编译应用程序时,即可看到运行中的本地代码编译器(对于从未见过Delphi执行本地代码编译的人来说,该过程的速度令人吃惊)。

执行下列步骤,即可改变每个窗体上的栅格点之间的距离:

1.重复上例中的步骤12

2.在Preferences属性页的Form designer组中,把Grid size XGrid size Y修改到两个像素大小。单击OK按钮。

把栅格距离修改到两个像素,可更好地控制控件的位置。

1.1.6  使用上下文菜单

如果Delphi中所有的菜单项都放在菜单栏上,这可能会让人迷惑。那样就必须对它们中的许多菜单项调出或调入,因为它们只在特定的环境中才是必要的或有用的;由于菜单项很多,搜索冗长的列表可能会很困难。Delphi是成熟的产品,而Inprise在设计集成开发环境方面是专家,记住这一点是很重要的。出于看上去舒服的原因,有些东西藏到了不显眼之处。

当您的注意力集中于开发的某一方面时,很可能会注意到开发环境的相关方面。Delphi中的大部分区域都有相关联的菜单,只要把鼠标指针置于该区域并单击右键即可访问。通常,这些快捷菜单上是一些最有用的任务。

注意:由于Delphi是用其自身的语言——Object Pascal所开发的,因此凡是Delphi所提供的功能,均可添加到您的应用程序中。这样,您在Delphi中发现的特征都可以添加到您的应用程序中。快捷菜单就是一个例子。

代码编辑器上下文菜单功能的探索

在单元上单击鼠标右键,可找到一些有用的快捷菜单。该快捷菜单在帮助文档中称为代码编辑器上下文菜单(Code Editor Context Menu)。当鼠标指针位于源代码单元时,单击鼠标右键即可打开该菜单。任何单元都是可以的。本节中,我们将仔细察看几个较为有用的代码编辑器上下文菜单项。您也可以详细探究一下所有的操作,或用关键字code editorcontext menu检索帮助文档。

查找声明

当鼠标位于符号上时打开代码编辑器上下文菜单,菜单会显示Find Declaration菜单项。单击该菜单项,Delphi将打开引入该符号的源代码文件。例如,如果创建了一个新工程并在TForm符号上单击鼠标右键,Delphi将打开forms.pas单元并把光标置于声明TForm类的那一行代码上(对于Iliad——即Delphi 6 Build 2.6forms.pas文件,打开后光标置于672行)。

这对于查找关于某个类或单元的详细信息是一项有用的技巧,其中还包括了支持实现的源代码。

打开光标处的文件

大部分应用程序都由许多文件组成的。过一段时间,很容易忘记代码的位置甚至是文件的位置,但Delphi是不会忘记的。无须到源代码中查找某个特定的单元,单击uses声明中的单元名,打开Code Editor Context菜单,单击Open File at Cursor或使用快捷键Ctrl+Enter(关于uses子句,参见1.4节“源代码文件的组织”)。

调试菜单的快捷方式

在代码编辑器上下文菜单中还有调试菜单项。其中许多与Run菜单中的调试菜单项相似。但Goto Address项在主菜单上并未发现,而Add Watch at CursorAdd Watch更加直接。打开Code Editor Context菜单,并按Goto AddressD,G键,将显示一个对话框。在Enter Address to Position对话框中输入内存地址,Delphi将对该内存地址打开CPU窗口。

打开Code Editor Context菜单,并按D,W键,将对当前光标位置添加一个观察项。Delphi将自动取得当前位置的符号或选定的文字,向Watch列表中添加一项并打开Watch列表。

1.2  创建应用程序

在对C语言及其发明者Brain KernighanDennis Ritchie的赞美声中,几乎每本书都把Hello World!作为第一个程序引入。出于趣味的原因,本节中的例子程序比传统意义上的第一个程序要复杂一些。本节将示范创建基于窗体的应用程序的基本步骤,和如何保存已有的工作、编译及运行应用程序。

注意:把一些控件放到窗体上、添加代码、运行程序,这只是黑客风格的编程方法。虽然许多应用程序是这样开发的,但对于开发健壮而具有可扩展性的产品,这种途径并非是个好办法。虽然最终都需要绘制出窗体,但独立地设计解决问题的类并使之与图形用户界面分隔开来将产生更好的最终产品。

1.2.1  创建一个程序

计算机程序可能简单,也可能复杂。其区别一般在于实用。一个非常简单的程序可能对于学习目的非常有用,但没有其他用处;而一个设计良好的复杂程序可能会解决一整个业务上的问题。在本例中我们的目的是教育和示范,因此程序很简单。

Visual Basic和类似的程序一样,Delphi也是个快速应用程序开发(RADRapid Application Development)工具。快速应用程序开发正在变成黑客风格编程的同义词。开发者通过RAD工具可快速创建可视化产品。问题在于可视化的外观经常会与其实质相混淆,而反过来则是不对的,即实际物质无须外观即可被识别。对于我们的目的,黑客风格的编程是不允许的。

按下列步骤,可创建增强版本的Hello World程序:

1.启动Delphi 6

2.屏幕上将显示空白窗体,请确认窗体位于前台。沿工具栏顶部可以看到几个属性页。其中Standard属性页被选定(如果不是这样,请选定它使之成为当前属性页)。

注意:这一组属性页称为组件面板,它包含了许多Delphi开发者可用的可视化和非可视化组件的可视化外观(更多信息,请参照Delphi帮助文件中的An Introduction to the Visual Component Library一节)。

3.双击Standard面板的RadioGroup图标。它是右起第三个组件。这将放置一个RadioGroup在前台的窗体上。

4.按F11键打开Object Inspector

5.在Object Inspector顶部的对象选择器中,选定RadioGroup1

6.请记着Object Inspector中的属性是按字母顺序排列的,向下滚动直至找到Items特性。双击Items特性打开String List Editor(见图1.22)。

1.22  Object Inspector中的RadioGroup1对象

       Items特性的String List Editor视图

7.在String List Editor中键入Geek,PC Gamer,EnglishGerman,各占一行。按Enter键来分隔各项。

8.在Object Inspector中,对Caption特性键入Customary Greeting

9.将RadioGroup1组件的ItemIndex特性改变为0ItemIndex属性表示选定的项,0表示第一项,1表示第二项,依此类推。

10.单击Object Inspector中的Events属性页。确认在对象选择器中已选定RadioGroup1对象,双击相邻的OnClick事件的右侧栏以生成事件过程。

11.在代码编辑器中,将下列代码输入到beginend两行之间。

Case RadioGroup1.ItemIndex of

  0: ShowMessage('Hello World!');

  1: ShowMessage('Welcome to Valhalla Tower Material Defender!');

  2: ShowMessage('Hi.');

  3: ShowMessage('Guten Abend.');

  else

    ShowMessage('item not found');

end;

提示:Tab键并输入特性或事件名,可在Object Inspector中快速移动到特性或事件。Delphi将执行对特性的搜索。

1.2.2  保存您的工作

要按照自然的节奏来保存您的工作。经常保存是个好的经验法则。对我们的例子来说,我们需要保存所有的文件并给工程命名,在编译时它将变成为可执行文件的名字。按键Shift+Ctrl+S键可保存进行中的工作,而按Alt+F,v键将保存所有的文件。把源代码单元命名为main,这将分别把源代码和窗体文件保存为main.pasmain.dfm。将工程命名为Hello World

提示:除了那些最简单的应用程序以外,请使用版本控制程序。即使对于本书中的简单例子,我也使用了SourceSafe,因为例子对本书是很重要的。有许多版本控制程序,包括:PVCS,SourceSafe,Harvest,StarTeam及ClearCase等。

下一步是测试您的应用程序。在此之前需要编译并运行它。

1.2.3  编译并运行程序

有两种方法来建立可执行文件。一种是按Alt+P,B键来运行Project,Build菜单项,另一种是按F9键,Run,Run命令。Delphi是一个编译环境,程序必须先编译,然后才能运行。

注意:如果看一下Windows资源管理器,可以注意到这个简单的应用程序在编译后大约为330k。原因是在很基本的应用程序中也存在这一些大而复杂的对象,包括窗体和应用程序类等。但经过这一次跳跃式的上升之后,当加入更多功能时,Delphi应用程序的大小增长极为缓慢。

F9键可以运行示例程序。单击所有的单选按钮以确保从每一个都得到正确的问候。1.2节“创建应用程序”中的三个小节描述了一系列的任务——也是一个很规则的循环,随着应用程序的复杂性的增长,您可以一遍又一遍地使用该循环来测试应用程序中的模块。

1.3  理解Delphi的设计——以工程为中心

Delphi是以工程为中心的开发产品。这意味着每个应用程序都是一个工程,由一个或多个文件以及工程文件组成。组成工程的几种文件包括:源代码、窗体、编译过的单元、配置、选项、包以及备份文件。本节中,我们将仔细察看工程中的各种不同文件及其使用情况。

1.3.1  工程文件

工程文件具有.dpr扩展名,在本质上它包含了应用程序的入口点,位于beginend之间。前一节的程序的工程文件如下所示:

HelloWorld.dpr

program HelloWorld;

 

uses

  Forms,

  umain in 'umain.pas' {Form1};

 

{$R *.RES}

 

begin

  Application.Initialize;

  Application.CreateForm(TForm1, Form1);

  Application.Run;

end.

Project菜单选择View Source可以看到工程源代码。上面列出的代码都是由Delphi自动添加的。program语句指出了可执行文件的名字。uses子句后部是逗号分隔的列表,包含了所有显式包括在工程中的文件。$R语句是编译器指令。编译器指令{$R *.RES}指示Delphi在与工程同名、扩展名为.RES的文件中,查找Windows资源信息。beginend.对与C中的main()子程序、Visual Basic的起始过程等价。典型的Delphi应用程序以Application.Initialize开始,以Application.Run结束。

修改.dpr文件中的代码是可以的,但通常这并不必要。除非确实有修改它的原因,否则最好还是让Delphi来管理工程源文件。

1.3.2  源代码文件

Pascalwadc文件具有.pas扩展名。通常每个工程至少有一个单元。单元供编写代码之用。如果创建了窗体或数据模块,将会得到.dfm.pas两个文件。1.2.1节“创建一个程序”示范了如何在单元中编写代码。

1.3.3  窗体与数据模块

窗体与数据模块具有.dfm扩展名,并且与.pas文件相关联。代码实际写在具有.pas扩展名的源代码单元中。DFM文件原来是二进制文件,但在Delphi 5之后已成为脚本化的文本文件,其中定义了一些资源,使得窗体和数据模块能够存储对象的可视化外观。窗体是TForm的子类,数据模块是TDataModule的子类,二者都定义在forms.pas单元中。

如果想看一看窗体的持久化脚本,在Hello World应用程序中先把窗体置于前台,在窗体上单击鼠标右键,显示窗体上下文菜单,并选择View as Textmain.dfm的资源脚本列出如下。

main.dfm

object Form1: TForm1

  Left = 244

  Top = 138

  Width = 783

  Height = 540

  Caption = 'Form1'

  Color = clBtnFace

  Font.Charset = DEFAULT_CHARSET

  Font.Color = clWindowText

  Font.Height = -13

  Font.Name = 'MS Sans Serif'

  Font.Style = []

  OldCreateOrder = False

  PixelsPerInch = 120

  TextHeight = 16

object RadioGroup1: TRadioGroup

  Left = 296

  Top = 208

  Width = 201

  Height = 193

  Caption = 'Customary Greetings'

  ItemIndex = 0

  Items.Strings = (

    'Geek'

    'PC Gamer'

    'English'

    'German')

  TabOrder = 0

  OnClick = RadioGroup1Click

  end

end

在上面列出的文本形式的main.dfm结尾处,可以看到在例子程序中输入的Item.Strings数据。这就是.dfm文件,它存储了一些描述窗体文件和数据模块的数据。

1.3.4  配置与选项文件

当您改动Project,Options设置时,Delphi把改动存储在一个具有.dof扩展名的文件中。当所做的改动影响到了应用程序的编译方式时,这些改动将以文本形式存储到.cfg文件中或配置文件中。

实际上分别生成了几个文件来存储配置、选项、To-Do和其他类型的信息。如果在Help Topics对话框的Find属性页中查找Generated files,即可得到关于存储工程信息的所有生成文件的信息。大部分情况下,Delphi自动管理这些文件。请勿删除它们。

无须记住所有的文件及其作用,只需记住一条规则:只能删除扩展名中带有~(波浪线)的文件、.dcu文件或者不需要的文件。如果使用了SourceSafe这样的版本控制产品,即使误删了需要的文件,也可从SourceSafe中将其恢复。

1.3.5  中间编译单元

编译过的单元是不可执行的,以.dcu为扩展名。在建立应用程序的链接阶段,所有的.dcu文件链接起来成为可执行程序。这些文件是可以删除的,但最好还是让Delphi来管理这些文件。

注意:如果只分发DCU文件,编译过的代码在Delphi将来发布的版本中可能是无效的。如果不升级,程序员就无法继续使用这些DCU文件。如果您出售的是工具软件,可以考虑公开源代码并在许可协议中规定可接受的使用方式。

当您建立应用程序时,Delphi会把源代码文件与编译过的单元进行比较。如果源代码没有被修改过,Delphi就不需要重新编译源文件。如果希望其他的开发者使用您的产品来建立应用程序,您可以只发布.dcu文件,而不发布源文件。以这种方式,其他的开发者可以使用您的代码,而无须确切知道代码是如何编写的。发布.dcu文件确实是一种方法,这样您可以传播私有代码而无须将其公开。

1.3.6  备份文件

无论何时,只要您改动了一个文件并且进行了保存,Delphi都会对该文件已存在的版本进行备份。这样,您就拥有该文件的最新版本以及一个稍早一点的版本。对备份文件的命名惯例是:它的名字与原文件几乎相同,但在‘.’与扩展名的第一个字母之间插入了一个~(波浪线)。例如,main.pas就变成了main.~pas

由于备份文件不会以其他方式修改,因此要恢复备份,只要在资源管理器中重命名备份文件,去掉其名字中的波浪线即可。如果您开始开发后一直使用某种版本控制产品,在更新文件的已归档版本时经常保存,那么不会丢掉任何修改。

1.3.7  包文件

包文件是一种特殊的工程,扩展名为.dpk。包文件是一种定义组件包的工程。本书后面还会有更多的关于组件和包文件的知识。

1.3.8  应用程序文件

您可能已经熟悉了几种基本类型的应用程序文件,Delphi都能够创建所有种类的应用程序文件。包括动态链接库(.dll)、可执行文件(.exe),ActiveX控件(.ocx,因为以前ActiveX支持的是OLE命名规范)。

其中每一种文件都代表一种终端产品,又经过编译和链接的代码组成。可执行应用程序是单独运行的程序或进程外服务器。动态链接库代表资源文件或进程内服务器,而ActiveX控件是用于建立其他程序的支持性控件。

1.4  源代码文件的组织

经常使用的最重要的文件是源代码文件,称为单元。没有了单元,窗体文件只是一种画出图像的复杂方法。理解单元的各种不同方面是很重要的。这将有助于您理解在何处写代码、为什么写这些代码。参照下面的代码,我们将继续对单元的讨论。

Unit1.pas

unit Unit1;

interface

uses

  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs;

type

  TForm1 = class(TForm)

  private

    { Private declarations }

  public

    { Public declarations }

  end;

var

  Form1: TForm1;

implementation

{$R *.DFM}

initialization // user must add

finalization

end.

在本节的其余部分,您将看到示范单元的各个部分的代码片段。如果您想知道在单元的整体中这些片段的位置,请参照前面的空单元的完整列表。该单元是一个有相关窗体的单元。请记住,单元不必有相关的窗体,反之则不然。

1.4.1  单元的各个部分

单元包括单元名和接口部分,接口部分中包含了类型声明、变量声明,如果需要还会有常数。单元的下半部分,即implementation之后,是实现部分,该部分可包含类型声明、变量声明、常数和过程。通常在实现部分会看到代码。

如果单元与数据模块或窗体关联,紧接关键字implementation之后是$R编译器指令,用于查找资源。关键字end.标志了文件的结尾。可以选择加入关键字initializationfinalization,但它们不会被自动添加。initialization代码在单元中的所有其他代码运行前运行,finalization代码在单元中所有其他代码运行后运行。单元装载到内存后,马上运行initialization部分,而finalization代码刚好在单元被卸载之前运行。

VCL源代码进行查找,只发现了大约100initialization。它是一种强大的工具,您很少需要用到它,但如果您看过Delphi的源代码如forms.pasclasses.pas,就能够找到一些使用它的方法。

1.4.2  Unit语句

Unit语句中包含了文件的名字。除了Windows文件系统存储文件时需要文件名以外,还可以把单元名作为定义名字空间的机制。例如,有一个单元名为math,其中有个过程名为Multiply,另一个单元名为Tribbles,其中也有一个名为Multiply的过程;为区分这两个过程,在调用Multiply时,可以把去掉扩展名的单元名作为前缀。

这样,在解析Math.Multiply调用时,编译器将找到math单元中的Multiply;而另一个调用Tribbles.Multiply将对应到Tribbles单元中的过程。unit语句的形式是unit 文件名;其中文件名是在保存文件时由Delphi管理的,不包括.pas扩展名。

1.4.3  接口部分

可以认为单元分为两部分。上半部为接口部分,起始于包含关键字interface的那一行,结束于关键字implementation之前,其余为第二部分。在最简单的意义上,这两部分的作用是互补的。上半部,或接口部分,描述了应用程序的其余部分在该单元中可以访问哪些东西。下半部,即实现部分,通常是编写运行代码之处(对于实现部分的更多细节,请参见1.4.4节“实现部分”)。

最重要的是要记住:接口部分没有运行代码,但包含了其他单元可以访问的类型、常数和变量等。它也描述了该单元中可调用的过程和可使用的数据。

1.4.4  实现部分

实现部分是编写运行代码之处。也可以包含类型、变量和常数。定义于接口部分的变量、类型和常量可以在单元外使用,与此相反,在实现部分定义的只能在单元内部使用。

注意:过程或函数的声明是不包含代码体的语句,即不包含begin和end语句及二者之间的代码。过程性声明放在接口部分。

注意:过程或函数的定义包含声明部分和函数体,即实际的代码。过程性的定义放在实现部分。

另外,定义在实现部分的过程和函数,如果在接口部分没有相应的声明,则只能在单元内部使用。如果希望其他单元可以访问过程和函数,则要将其声明放在接口部分而将定义放在实现部分。

1.4.5  定义Uses子句

Uses子句指示编译器添加在列出的各个单元中找到的代码。可将该语句在接口部分和实现部分各放一个。如果用到Uses子句,它将紧跟在interfaceimplementation关键字之后。

警告:如果Unit1需要Unit2的代码,Unit2也需要Unit1的代码,则在Unit1和Unit2的Uses子句中将相互列出对方。但Unit1和Unit2不能在同一部分相互引用。例如,在接口部分Unit1可引用Unit2,Unit2也可引用Unit1;但Unit1和Unit2不能在接口部分相互引用,这是个循环引用错误。而在实现部分两个单元可以相互引用。

注意:所有的单元都隐式引用了system.pas单元。system.pas单元无法显式引用。

如果改变了Unit1接口部分的Uses子句,而Unit2引用了Unit1,那么Unit1Unit2可能会被重新编译。如果Unit1在接口部分引用了Unit2,而改变了Unit2实现部分的Uses子句,则Unit2必须重新编译,而Unit1则无需如此。

1.4.6  Type子句

接口部分和实现部分都可能有Type子句。按惯例,大多数Type子句都位于接口部分。类型声明可以定义集合、数组、记录和类等。在关键字type之后可引入新的类型。下面列出的代码示范了类型定义的例子。

type

TForm1 = class(TForm)

private

{ Private declarations }

public

{ Public declarations }

end;

提示:在单元与窗体关联的情况下,可能只有代表窗体的一个类型定义。有一条规则:每个单元中只能有一个窗体定义。但即使是与窗体相关联的单元,每个单元中也可以定义多于一个的类。看一看VCL的源代码,可以注意到在许多情况下,Delphi开发者都在一个单元中定义了多个类。

上面的代码片段由本节开始处列出的代码中撷取而来,包含了TForm1类的类型定义。关于上面列出的类定义的各个方面,更多的信息请参照第2章“学好面向对象的Pascal”。

1.4.7  变量部分

接口部分和实现部分均可包含Var子句。当要定义其他单元可访问的变量时,请把这些变量放在接口的变量部分,然后在使用变量的单元的Uses子句中包含该单元即可。接口部分定义的变量可认为是全局变量,请谨慎使用。由于无法确保全局变量不被其他的程序员误用,所以加入这段说明以防止误解。

实现部分定义的变量只能在所定义的单元内访问。他们被称为本地变量,在单元内可随意访问,但不能被使用该单元代码的其他单元所引用。这样,只有包含了该变量定义的单元的作者才有可能误用本地变量。本地变量比全局变量优先选用,但并不理想。

1.4.8  资源声明

在本节开始列出的代码中可看到{$R *.res}编译器指令,我们提到过,该指令指示编译器包含与该单元同名的.res文件。$R指令通常只出现在具有窗体的单元中,它们也可能是开发者因为某种原因添加的。

提示:对于运行时加载图形资源,mplayer.pas中的TMediaPlayer类是个好例子。可以利用Delphi所带的Image Editor创建图像,再借用TMediaPlayer类中的代码向您的应用程序中动态加载位图。

定义具有可视外观的新组件时,可加入资源指令。一个例子可在如图1.23TMediaPlayer的代码中发现。媒体播放器上的按钮是位图,必须存储于某处,即Windows资源文件中。TMediaPlayer 的源代码是 mplayer.pas 其中 implementation 关键字的下一行就是{$R MPLAYER}。查找mplayer.res文件,可以发现它在Lib目录下。

1.23  TMediaPlayer组件

1.4.9  Initialization部分的使用

单元的initialization部分的代码将在单元中任何其他代码运行前运行。在initializationfinalizationend关键字之间的代码,将在单元向内存加载时运行。如果要使用全局变量或本地变量,可在initialization关键字后进行初始化。

1.4.10  Finalization部分的使用

如果单元中有initialization部分,也可以使用finalization部分。可在finalization部分运行清除代码,并释放在initialization部分分配给对象的内存。finalization部分由关键字finalization开始直到关键字end(文件结尾)。finalization部分与对应的initialization部分按相反的顺序运行。例如,Unit1,Unit2,Unit3按序装载入内存,则其finalization部分将按Unit3,Unit2,Unit1的顺序运行。

1.5  代码的原子与分子

Object Pascal的基本组成部分与其他语言是相同的。它们由操作符、操作数和少量的关键字构成。这些原子性的成分按Object Pascal语法所规定的顺序组合起来,就产生了语句。语句是可执行代码的最小片段。如果您正由另一种语言转向Object Pascal或希望回顾一下,请继续阅读本节其余部分,否则可跳至下一节。

1.5.1  操作符与操作数

Object Pascal, C++, Visual Basic, Java以及大多数现代程序设计语言中,操作符与操作数的用法是类似的。操作符是可用作表达式一部分的符号或记号。操作数是操作符执行其操作的数据。

有像Not操作符一样的单目操作符。Not操作符对操作数的值取反。如果操作数是布尔值,则结果也是布尔值。如果对整数执行了Not操作,则对该整数的值按位取反。单目操作符有一个操作数,位于操作符的右边,例如Not True。双目操作符有许多,如+ ,,/ ,div ,= :=等。双目操作符有两个操作数,分别位于其左右两边。例如,/ 代表浮点除法。无论操作数是整数还是浮点数,/ 操作的结果都是浮点数。div操作符要求两个整数,也返回整数。

Object Pascal中,= 操作符是双目操作符,它检测是否相等;:= 是赋值操作符。例如,向整数变量 A 赋值 5,应写为 A := 5。要计算同一个整数A 是否等于5 ,应写为 A = 5,例如:

if ( A = 5 ) then …

Object Pascal具有其他语言中绝大部分的通用操作符。在帮助文档的索引中查找Arithmetic operators,就可找到关于算术操作符的详细信息。Delphi还有指针操作符、位逻辑操作符、算术模操作符等。在Object Pascal中没有三目操作符(像C语言引入的三目操作符 ?= )。

9种操作符:算术操作符、布尔操作符、逻辑操作符、字符串操作符、指针操作符、集合操作符、关系操作符、类操作符和@操作符。当打算使用一种特定类型的操作符,而以前又没有用过时,可以查找帮助文件获得有关的详细信息。请记住:操作符具有优先级顺序,即默认情况下操作符的计算顺序。请使用括弧表示您所需要的操作顺序,或参照Delphi帮助中的操作符优先规则获取特定的信息。

1.5.2  关键字

记号是程序中有意义的最小单位。记号由空格、制表符、注释、分隔符(如分号)所分隔。因此操作符和Object Pascal的关键字都是记号。关键字是一些单词,它们被保留用作Object Pascal语言的一部分。在Object Pascal的关键字(或保留字)的列表中,包含大约75个记号。还有40个词保留作为指令,两个词具有特殊意义。对于保留字的完整参考资料,请参考Delphi的帮助文档。在索引中查找reserved words and directives,可找到关于保留字和指令的详细信息。

完整的Object Pascal语言由语法规则、9种操作符、大约120个保留字和指令构成。关于语法规则的参考信息,请在帮助文档中查找grammarObject Pascal 语法)。

1.5.3  基本数据类型

Object Pascal包括了大多数Windows编程语言中的基本数据类型:integerdoublestringcharBoolean。这些是最常用的,各个类型还有其他特定的版本。数据类型用于声明变量。变量声明的规范形式如下:

VarDecl -> IdentList ':' Type [(ABSOLUTE (Ident | ConstExpr)) | '=' ConstExpr]

注意:在与上面类似的语法规则中,按照惯例,由方括弧括起的部分是可选的。因此过程不一定要有参数。

简化的规范形式是 varname: type,其中varname是一个有效的名字,不是关键字,type是预定义或用户定义的数据类型。例如,string是预定义类型,TForm1是用户定义类型。String变量声明的例子如下:

UserName : String;

TForm1变量声明的例子如下:

Form1 : TForm1;

absolute关键字强制性地使变量位于某个绝对内存地址。如果常量表达式是数字,该数字就表示了地址。如果常量表达式是变量,绝对地址就是该变量的地址。考虑下面的例子程序:

Absolute.dpr

program Absolute;

uses

Forms,

SysUtils,

Dialogs;

{$R *.RES}

var

A : Integer;

B : Integer Absolute A;

 

begin

Application.Initialize;

A := 1007;

ShowMessage(IntToStr(B));

Application.Run;

end.

该程序声明了两个整数:A BB的定义方式使得其地址与A的地址相同。向A赋值的效果与向B赋值相同。

关于特定种类的数据类型的信息,请在帮助文档中查找integercharacterBooleanenumeratedstringreal等类型。

1.5.4  书写代码

Object Pascal的语句可以这样书写:使用基本的操作符和类型正确的操作数,后跟分号作为语句结尾。含有关键字的语句结尾也有分号。

语句就是beginend间所包含的可运行代码。前一节中的程序,DPR文件的beginend之间共有4个语句。语句的例子,请参见1.4节“源代码文件的组织”。

1.5.5  条件语句

在最常用的一种语句中,使用条件测试来决定执行哪一条语句。有两种类型的条件语句:if .. then .. else case .. of 语句。

if条件语句

if .. then .. else 语句规范形式如下:

IfStmt –> IF Expression THEN Statement [ELSE Statement]

其中Expression表达式返回布尔值。围绕else子句的方括号表明它是可选的。If else 子句的statement部分既可以是单一语句,也可以是复合语句。简单语句由一条语句构成,复合语句由几条语句包含于同一beginend块中构成。下面列出了几个语句的例子。

var

HairColor : TColor;

DateOfBirth : TDateTime;

Year, Month, Day : Word;

begin

HairColor := clGreen;

 

If (HairColor = clBlack) then

ShowMessage('Adding to suspect list');

 

DateOfBirth := StrToDate('02/12/1966');

DecodeDate( Now, Year, Month, Day );

 

If( DateOfBirth <= EncodeDate( Year - 21, Month, Day )) then

ShowMessage('Legal')

else

ShowMessage('Dial 9-1-1');

 

If( DateOfBirth = EncodeDate(Year - 21, Month, Day )) then

begin

ShowMessage('First Drink is Free');

end;

end;

请记住,只有在语句的最后结尾处才需要分号。如果有else子句,那么语句的结尾是在else子句之后。如果没有else子句,分号将置于if .. then子句的语句结尾处。语句可以是简单的,也可是复合的,两种语句的分号放置规则是相同的。复合语句由begin end块表示。

根据惯例,如果有两个以下的分支可能性,那么if条件语句应当是最好的选择。但是,如果有两个以上的分支条件,使用case语句将更好。以下是case语句的三条规范:

CaseStmt -> CASE Expression OF CaseSelector/';'... [ELSE Statement] [';'] END

CaseSelector -> CaseLabel/','... ':' Statement

CaseLabel -> ConstExpr ['..' ConstExpr]

提示:Delphi中,Expression可以是字符串表达式。Delphi并不像Visual Basic那样把case语句的表达式类型限制为有序类型值。

第一条规则CaseStmt,将case语句描述为计算一个表达式的值并将结果与CaseSelector中列出的值比较;默认的匹配条件是可选的else子句。CaseSelector包含一个或多个以逗号分隔的CaseLabel,后跟冒号和一条语句。CaseLabel规则包含一个或二个ConstExpr(常量表达式),其中两个ConstExpr .. , 连接,表示一个按升序排列的连续值的范围。如下所示:

// Example Case Statement

var

HairColor : TColor;

begin

HairColor := clBlack;

case HairColor of

clBlue: ShowMessage('Grandma');

clGreen: ShowMessage('Teenager');

clRed: ShowMessage('step child');

clBlack, clGray: ShowMessage('Dad');

1000..10000, 10002: ShowMessage('Mom''s bad hair coloring');

else

ShowMessage('not in my family');

end;

end;

从列出的代码可以看出,case语句可包含逗号分隔的一组范围或表达式。如果有许多分支,那么case语句比嵌套条件语句更容易阅读。

1.5.6  循环控制语句

Object Pascal中有3种循环控制机制:repeat .. untilwhile .. do,和for .. do语句。与其他语言相同,while .. do语句可能循环零次到多次。repeat .. until语句可能循环一次到多次,因为条件测试位于循环体的末端;for .. do语句将按for子句中指定的次数进行循环。

有个很简单的惯例:如果需要循环至少一次,请使用repeat .. until语句;如果希望循环零次到多次,请使用while .. do语句;如果循环指定的次数,请使用for…do循环语句。除了while..dorepeat..until循环过程的最少次数外,其测试条件也有所不同。如果要执行相同的条件测试,则while表达式和until表达式的值必须相反。对于各种类型的循环,请看下面的示例代码:

// Example Loop Control Code

var

I : integer;

begin

 

I := 0;

while( I < 10 ) do

begin

MessageBeep($FFFFFFFF);

Inc(I);

end;

I := 0;

repeat

MessageBeep($FFFFFFFF);

Inc(I);

until ( I >= 10 );

 

For I := 1 to 10 do

MessageBeep($FFFFFFFF);

end;

上面的每个循环都运行了指定的次数10MessageBeepWindows API过程,参数为一个无符号整数;$FFFFFFFF等价于-1。该语句使PC喇叭发出蜂鸣声)。如果要在循环体中处理多个语句,请将其包含在beginend之间;在上面的while..do循环的循环体中即可看到。对于repeat..until,无须使用beginend,但如果用了beginend,编译器仍然可以编译。

1.6  编写过程和函数

在程序的可运行代码中,语句是最小的有用组成部分。上一层最小的有用的抽象是过程一级,有FunctionProcedure(如果没有必要区别二者,Procedure既可指Function也可指Procedure;有必要时,将指出这一区别)。

注意:对于过程的编写有一些基本的定性的原则。通常,过程和函数不应过长,大多数情况下应该不超过25行左右。如果察看Delphi中VCL的源代码,可以看到大多数过程都相对较短。简短的过程易于维护,也有助于程序员把注意力集中于单一的任务;而长的过程则难于阅读、不易理解,因此维护起来较为困难。长的函数通常容易超出短期记忆的能力,因此难于写好。

1.6.1  编写过程

过程以关键字Procedure开头,后跟过程名,以分号结尾。下面的规范ProcedureHeading即说明了这一点。

ProcedureHeading -> PROCEDURE Ident [FormalParameters]

注意:语法规则比单独的代码实例更难阅读,不过一旦了解了规范形式,就能够确定代码的所有可能形式。理论上这意味着一个论断:如果您读过Delphi帮助中的语法部分,那么您就能够写出所有可能种类的语句。尽管查找Delphi的语法可能令人厌烦,但这可能有助于解决一些问题。

提示:解决语法问题的一种技术性稍弱的办法是:写出代码,试着进行编译。如果编译通过,那么它就是对的。

使用过程的语法规则的第一部分,下面是过程声明的实例。

Procedure Connect;

由规则可以看出,过程以关键字Procedure开头,后跟一个标识符,即过程名。在规则中清楚地列出了过程的可选部分,称为FormalParameters,是传给过程的参数。FormalParameters分为下列三部分:

FormalParameters -> '(' FormalParm/';'... ')'

FormalParm   -> [VAR | CONST | OUT] Parameter

Parameter    -> IdentList [':' ([ARRAY OF] SimpleType | STRING | FILE)]

-> Ident ':' SimpleType '=' ConstExpr

FormalParameters是这样定义的:外部是括弧,括弧内是由FormalParam构成的、被分号分隔的重复的列表。FormalParam以可选的关键字var,const,out开头(与参数修饰符相关的更多细节,请参照下一小节),后接Parameter规则。Parameter由包含一个或多个标识符的标识符列表、冒号、类型组成,或由标识符、冒号、等号、默认值组成。该规则非常灵活。下面有几个合法的过程声明的例子示范了这些规则。

// Procedure Declaration Examples

Procedure NoParams;

Procedure OneParam( I : Integer );

Procedure TwoParamsSameType( I, J : Integer );

Procedure TwoParamsDifferentTypes( I : Integer; S : String );

Procedure OneVarParam( var I : Integer );

Procedure OneConstParam( const D : Double );

Procedure OneWithDefault( Ch : Char = 'A' );

如果已经理解了procedure语句,您也就明白了function语句。下一节中可以看到,函数与过程之间只有微小的差别。

1.6.2  编写函数

函数的规范规则与过程几乎相同(参见前一节有关过程规则的信息)。不同之处在于,函数使用关键字function而且有返回值。函数被设计为向调用者返回数据。例如,上面列出的过程NoParams可重定义为函数,如下所示:

Function NoParams : Integer;

当比较FunctionHeadingProcedureHeading时,不同之处有:关键字function代替了procedure,‘:’(冒号,不带引号)以及返回类型的使用。

1.6.3  参数前缀的使用

可以给参数添加前缀var,const,out,或不加任何前缀。参数前缀的目的是限制参数的使用方式。好的想法是使用尽可能多的约束条件,以避免参数误用。

假定在过程内部从不修改某个参数。使用const前缀修饰该变量,则该参数在代码中不能被修改。如一个参数需要被过程修改,可用关键字var进行修饰。对于各种不同的前缀及其对变量用法的影响,下面的几个小节进行了概述。

传值参数

传值参数可在过程内部修改,但过程返回时该修改不会反映出来。不加任何前缀,就表示该参数为传值参数。

Procedure Foo( I : Integer );

I的值被传递到Foo过程。当Foo返回时,在Foo中对I的修改不会反映到I

传递引用参数

用引用传递参数意味着传递了指向实际数据的一个指针。如果用var前缀,即指定用引用传递参数。被调用过程会改变该参数,修改将对实际数据进行,过程返回时可以看到改变。

Procedure Foo( var I : Integer );

I作为变量传递。当Foo返回时,在Foo中对I的改变将反映到作为参数传递到Foo的实际变量。

 

 

传递常量参数

常量参数是不可改变的,即“可以看不能碰”。应尽可能地使用常量参数,因为常量参数是可信赖的。从调用过程前直到过程返回后,常量参数的值不会改变。

Procedure Foo( const I : Integer );

Foo中无法改变 I ,结果在过程返回时 I 的值不会有任何变化。

传递只写参数

只写参数以out关键字为前缀。out关键字是为了与COM接口兼容而引入的。对被调用的过程来说,当参数声明中含有out关键字时,该参数的值没有任何特定的意义,而过程应该向该参数赋值。调用者可以预期在过程返回时,out参数已含有需要的值。

Procedure Foo( out I : Integer );

传给Foo的值将被废弃掉,当过程返回时对I的改变将反映出来。在使用引用传递参数这一点上,outvar相似。out前缀是为支持COM而添加的。

1.6.4  参数的默认值

默认值提供了一定程度的灵活性。在参数后添加等号,并把合适类型的默认值置于等号右侧,就表示该参数具有默认值。在这种上下文环境中,等号的作用与赋值操作符类似。

当默认值有意义时,可使用默认参数。这样,在调用过程时,该参数就可用可不用。对于有默认值的参数,如果不传递数据,则其值为默认值,否则其值为传入的值。

Procedure Foo( I : Integer = 5 );

Foo;

Foo( 10 );

不提供参数即可调用Foo,也可为参数I提供一个新值调用Foo。不传递参数,I的值为5。而在最后一条语句中,I的值为10

当使用默认参数时,右侧参数必须在左侧参数之前具有默认值。这样,如果一个参数具有默认值,则其右侧的参数必定具有默认值(第4章中有许多参数使用了默认值的例子)。

1.7 

1.1.4节“运行程序”中,描述了一些内建在集成调试器中的调试能力。到现在为止您已经看过并且有机会来编写一些代码,但还有一些调试工具是您应该了解的。

集成开发环境允许传递命令行参数,以模拟控制台应用程序或从Windows NT Task Scheduler启动应用程序。除了命令行参数,Delphi还允许使用单行断点。在开发的每个阶段,这两项能力都使得您的程序易于测试。

1.7.1  向集成调试器传递命令行参数

Alt+R,P键可从Run菜单指定命令行参数。Run Parameters对话框可用于指定简单的命令行参数、用于指定测试服务器程序或ActiveX控件的主机应用程序、以及远程调试的主机。

在应用程序中读取命令行参数时,请使用ParamCount作为循环控制的上界,从ParamStr( )数组中逐个读取参数。for..do循环非常适合该目的。

var

I : Integer;

begin

for I := 0 to ParamCount-1 do

ShowMessage( ParamStr(I) ); // process parameter

end;

1.7.2  简化的中断命令

当设置断点时,编译器插入soft ice命令。即汇编语言指令中断3。这对您是不可见的。您所要做的只是按F5键来设置或取消断点。默认情况下,代码行的背景是红色时,就设置了断点。如果设置了断点,在IDE中运行到该行代码时,过程会挂起。

断点基础

通过对断点的调整,可以减轻调试和测试的负担。参见图1.24,按F5键就设置了一个断点,文件名、行号、断点的行为都已设置好。Condition使得您可以指定在当条件表达式为真时,中断执行。Pass count使得您可以在中断前执行指定传递数,例如在处理大量数据时可能出现在较为靠后的循环周期中的错误。Group(见图1.24)使得您可以把断点组织成组,这样就可以在Enable groupDisable group组合框中选择组的名字来设置或取消断点。

响应异常

Ignore Subsequent ExceptionsHandle Subsequent Expressions复选框作为一对来使用。选择前者将使Delphi在调试时忽略异常。如果选择了Handle Subsequent Exceptions,而且在Tools, Debugger Options对话框的Language Exceptions属性页中,选定Stop on Delphi Exceptions,则Delphi在遇到异常时将停下来。

把日志异常写到Windows NT事件日志

如果在Log Message域中输入一条信息,则当该断点挂起程序执行时,Windows NT事件日志中将添加一个条目。Eval Expression对写到日志条目中的表达式进行计算,如果Log Result也被选定,则计算结果也写入到NT事件日志中。

1.8   

本章特地为过渡型的程序员而设计的。如果您是一位中等程度的程序员,那么本章就Delphi 6中的各种特性已向您提供广泛而详尽的讲述。对于每个应用程序中都包含的那些从管理IDE、调试到编写一些基本代码的内容,本章示范了其中具有代表性的那些部分。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值