Delphi 学习笔记--卢炽光 2010-07-15
http://ce.sysu.edu.cn/hope2008/worklist/ShowArticle.asp?ArticleID=8631(转)
仅以我有限的项目经验总结Delphi学习中需要注意到问题。由于我有C#基础,故此会涉及C#和Pascal语法不同之处,通用的程序套路就不多说了。该文章涉及内容深度较浅,适合初学者阅读。如有错漏请包涵。
需要注意Delphi是一个IDE,它不是一个语言,它的性质和Visual Studio、Eclipse是一样的,是用来做开发的。Delphi是用Pascal做为开发语言,确切说是Object Pascal--支持面向对象的Pascal语言,当然Delphi可以用C++做开发,名字就换成了C++ Builder。无论用哪个语言,Delphi和C++ Builder提供的开发接口基本一致。
一、语法
1.变量定义
在Pascal语法中,变量必须先定义再使用,而且变量必须定义在类定义中或过程/函数体的var变量下。不允许变量定义在执行语句之间,这在程序设计时有好有坏。好在定义清晰,产看类定义和var就可以知道有多少变量,什么变量;不好在撰写程序时不方便。
变量定义举例如下:
type TUltiBase = class(TObject)
public
...
private
AVariable:String;
BVariable:Integer;
procedure GetAVariable();
end;
...
procedure TUltiBase .GetAVariable();
var
A:String;
B:Integer;
begin
...
end;
2.变量赋值
变量赋值使用:=运算符,初学者看来这个比较特别的,必须与=区分开。
在C#中,空类型使用null,在Pascal中使用nil。
注意,Pascal中不使用双引号,字符串是通过单引号括起来。
3.变量初始化和释放
用Delphi编写C/S程序册时候必须注意,大部分的控件或类型需要初始化和释放。如
procedure HandleDelete;
var
arrName:TStringList;
begin
arrName:=TStringList.Create;
...
arrName.Free;
end;
使用类型的Create构造函数初始化变量。类似于C#的
ArrayList arrName = new ArrayList();
但是C#有自动回收资源机制,所以一般情况下不需要释放资源,当然也可以手动释放
arrName.Dispose();
也可以通过
using(arrName){
...
}
自动让系统使用完后自动释放
比较Pascal和C#初始化变量,做个列表会更清晰:
|
Pascal |
C# |
初始化变量 |
arrName:=TStringList.Create; |
ArrayList arrName = new ArrayList(); |
带参数的初始化 |
win := TEditForm.Create(self); |
UserData data = new UserData("OO"); |
释放资源 |
arrName.Free; 必须 |
arrName.Dispose(); 不必 |
由于Pascal变量初始化后必须释放,而且为了避免中间出错,会形成这样一种结构:
CommentForm:= TCommentForm.Create(Self,humanID,commentID);
try
CommentForm.ShowModal;
finally
CommentForm.Free;
end;
4.运算符及变量比较
l 赋值 :=
l 比较相等 =
l 比较不等于 <>
l 比较大于 >
l 比较小于 <
l 逻辑与 and
l 逻辑或 or
l 逻辑非 not
需要说明:尽量在比较表达式外加括号(),以使条件表达更清晰
5.流程控制
a) IF Then Else End
需要说明:if、then 和end缺一不可。类似于C#的if、{ 和 }。
if (condition) then
begin
...
end
else if (condition2) then
begin
...
end
else
...
end;
b) While Do
while (condition) do
begin
...
end;
c) Repeat Until
repeat
...
until (condition)
d) Case Of
case index of
0: Result:= "AAAA";
1: Result:= "BBBB";
else
raise Exception.Create('超出索引');
end;
6.属性
属性的使用需要2~3部分:私有变量、写过程和属性定义。
type TUnitManager = class(TObject)
private
FYunmenHR:TYunmenHR;
procedure SetYunmenHR(value:TYunmenHR);
property YunmenHR: TYunmenHR read FYunmenHR write SetYunmenHR;
end;
...
procedure TUnitManager.SetYunmenHR(value:TYunmenHR);
begin
FYunmenHR:=value;
end;
...
property 属性名:属性类型 read 私有变量 write 写过程
如果需要只读或只写的属性,保留read 或write 即可。
7.函数
注意Pascal中的函数是function,具有返回值的。procedure不同,没有返回值。然而在C#中,过程procedure和函数function都是方法method,没有返回值则使用void,有返回值直接使用返回类型即可。
function GetItem(Index: Integer): TYunmenBase;
...
function TUnitManager.GetItem(Index: Integer): TYunmenBase;
begin
case index of
0: Result:= YunmenHR;
else
raise Exception.Create('超出索引');
end;
end;
function 函数名(参数定义):返回类型
返回值通过跟函数同名的变量或Result来传递如:
Result:= YunmenHR;
或者
GetItem:= YunmenHR;
8.过程
又称作过程,没有返回值。
procedure SetYunmenHR(value:TYunmenHR);
...
procedure TUnitManager.SetYunmenHR(value:TYunmenHR);
begin
FYunmenHR:=value;
end;
procedure 过程名(参数体);
过程定义时如果没有参数,则可省略括号(),调用的时候也可以省略括号。如
procedure HandleDelete();
procedure HandleGet;
...
procedure TUnitManager.HandleDelete();
begin
...
end;
procedure TUnitManager.HandleGet;
begin
...
end;
9.委托
Delphi的控件提供了很多事件,这些事件是通过委托实现的。如按钮的OnClick事件是定义在系统的按钮类中的一个委托过程。提供了参数和返回类型,其动作是预先定制好的。事件带来了很多好处,而且使用起来非常方便:
procedure Act_SaveExecute(Sender: TObject);
procedure TMainFrm.Act_SaveExecute(Sender: TObject);
begin
...
end;
调用起来很方便
Act_Save.OnClick = Act_AddPictureExecute;
在Delphi中动态绑定事件十分常见。
10.异常处理
跟C#类似,可以使用try捕获异常,但捕获异常的关键字和语法有些区别:
try
...
except
on E: Exception do
begin
...
end
end;
不允许使用try except finally end;的语法,而必须通过嵌套实现如:
try
try
...
except
on E: Exception do
begin
...
end
end;
finally
...
end;
二、OO特性
跟其他语言类似,只能继承一个类,但可以继承多个接口。但是delphi中没有抽象类,只能够采取折中方案,在类中声明虚拟的抽象过程/函数。
type TYunmenBase = class(TObject)
public
procedure AddObject;virtual;abstract;
procedure DeleteObject(ID:Integer);virtual;abstract;
procedure RefreshGridData;virtual;
end;
AddObject和DeleteObject都是抽象过程,通过关键字abstract标识,而且是虚拟的virtual,允许继承类复写。过程RefreshGridData不是抽象过程,但允许在继承类中复写。
type TYunmenHR = class(TYunmenBase)
public
procedure AddObject;override;
procedure DeleteObject(ID:Integer);override;
procedure RefreshGridData;override;
end;
TYunmenHR 继承类TYunmenBase,复写过程AddObject、DeleteObject和RefreshGridData,通过关键字override标识。
实现了类继承和过程复写后,就可以实现OO中的多态。
type TYunmenBase = class(TObject)
public
procedure AddObject;virtual;abstract;
procedure DeleteObject(ID:Integer);virtual;abstract;
end;
type TYunmenHR = class(TYunmenBase)
public
procedure AddObject;override;
procedure DeleteObject(ID:Integer);override;
end;
type TYunmenEdu = class(TYunmenBase)
public
procedure AddObject;override;
procedure DeleteObject(ID:Integer);override;
end;
type TYunmenEvent = class(TYunmenBase)
public
procedure AddObject;override;
procedure DeleteObject(ID:Integer);override;
end;
实现多态:
function GetItem(Index: Integer): TYunmenBase;
property Item[Index: Integer]: TYunmenBase read GetItem;
...
function TUnitManager.GetItem(Index: Integer): TYunmenBase;
begin
case index of
0: Result:= YunmenHR;
1: Result:= YunmenEdu;
2: Result:= YunmenEvent;
else
raise Exception.Create('超出索引');
end;
end;
使用TUnitManager的时候只需要使用不同索引Item即可访问不同的继承类。
var
util:TUnitManager;
begin
util:=TUnitManager.Create;
util.Item[0].AddObject;
util.Item[1].DeleteObject(1);
util.Free;
end;
三、常用过程/函数
1.字符串操作
a) Trim、 TrimLeft 和 TrimRight
Delphi中没有类似C#的
string s = " More important than what is when. ";
string s2 = s.Trim();
// s2 : "More important than what is when."
这种直接在字符串后使用Trim()或其他函数的功能。
不过它提供Trim的全局过程:
string s := " More important than what is when. ";
string s2 := Trim(s);
// s2 : "More important than what is when."
Trim()是去除字符串前后的空格和不可见字符,同理TrimLeft()是去除字符串左边的空格和不可见字符,TrimRight()去除字符串右边的空格和不可见字符。
b) Pos/PosEx
function Pos(Substr: string; S: string): Integer;
返回字符串S中子字符串Substr的索引,如果没找到,则返回0。
var S: string;
begin
S := ' 123.5';
{ Convert spaces to zeros }
while Pos(' ', S) > 0 do
S[Pos(' ', S)] := '0';
end;
function PosEx(const SubStr, S: string; Offset: Cardinal = 1): Integer;
从指定位置Offset开始,返回字符串S中子字符串Substr的索引,如果没找到,则返回0。如果开始位置是1,则功能和Pos一样。
c) Copy
function Copy(S; Index, Count: Integer): string;
function Copy(S; Index, Count: Integer): array;
从指定位置Index开始,返回字符串S中Count个字符。
d) Delete
procedure Delete(var S: string; Index, Count:Integer);
从指定位置Index开始删除字符串S中的Count个字符。
var
s: string;
begin
s := 'Honest Abe Lincoln';
Delete(s,8,4);
{ 'Honest Lincoln' }
end;
e) UpperCase 和 LowerCase
function UpperCase(const S: string): string;
转换字符串为大写或小写。
var
I: Integer;
begin
for I := 0 to ListBox1.Items.Count -1 do
ListBox1.Items[I] := UpperCase(ListBox1.Items[I]);
ListBox1.Items[I] := LowerCase(ListBox1.Items[I]);
end;
2.类型转换
a) StrToInt 和 StrToIntDef
function StrToInt(const S: string): Integer;
将字符出转换为数字,如果转换失败,则抛出EConvertError 异常。
function StrToIntDef(const S: string; const Default: Integer): Integer;
带默认值的StrToInt ,如果转化失败,则返回默认值
b) StrToBool 和 StrToBoolDef
function StrToBool(const S: string): Boolean;
将字符串转换为布尔值,如果转换失败则抛出EConvertError异常。
function StrToBoolDef(const S: string; const Default: Boolean): Boolean;
c) IntToStr
function IntToStr(Value: Integer): string; overload;
function IntToStr(Value: Int64): string; overload;
将数字转化为字符串。
3.应用程序信息
获取应用程序所在路径:ExtractFilePath(Application.ExeName)
获取应用程序文件名:ExtractFileName(Application.ExeName);
4.全局过程
a) Close、Halt和Application.Terminate都可以退出程序。但有些区别,当Close是一个主窗体时,程序会退出. Close会发生FormClose事件,FormCloseQuery事件. Halt会发生FormDestory事件, Application.Terminate以上三个事件都不会发生。
b) Exit,从当前过程退出。相当于C#的return。
2.控件常用事件
a) OnClick,控件点击事件
b) OnChange,控件的焦点改变事件
c) OnExecute,TAction中的触发事件
d) FormCreate,窗体创建时的事件
e) FormShow,窗体显示时的事件
f) FormClose,窗体关闭时的事件
g) AfterScroll,TADOQuery改变记录行触发的事件
h) OnGetDataText,TcxGridDBColumn显示到屏幕时触发的事件
i) OnPageChange,TPageControl页面切换触发的事件
j) OnKeyUp,键盘释放触发的事件,可用于判断输入的字符
k) OnCellClick,TcxGridDBTableView单元格点击事件
四、软件部署
1.命名
生成的程序以项目名命名。
2.发布位置
项目->选项->目录/条件->输出目录,方便部署软件。
3.软件版本及其详细信息
项目->选项->版本信息,填写软件版本信息
4.软件包
多个项目可以通过软件包管理。当时每个项目仍然对应一个程序。当设定“发布位置”后,所有程序都可以发布到同一个目录下,更便于部署。
5.异常调试
在需要调试的行左边添加断点(或按F5),运行程序(F9),当程序运行到断点时,程序进入中断状态,可使用执行到下一行(F8),跳过当前断点(F9)切换至不同断点或退出调试。调试过程中,按Ctrl+F7进入实时查看变量窗口,输入变量可返回当前值;按Ctrl+F5进入监视窗口,Ctrl+a添加监视点用以监视当前变量的值。
五、第三方控件
1.正则表达式
类:RegExpr.pas
引用:RegExpr
调用
var
RegExpr:TRegExpr;
FileNameAff:string;
begin
RegExpr:=TRegExpr.Create;
try
RegExpr.Expression :='http://www\.(\w*?)\.(.*)';
if RegExpr.Exec(edt_Url.Text) then
FileNameAff:= RegExpr.Match[1]
else
FileNameAff:= 'untitled';
finally
FreeAndNil(RegExpr);
end;
调用多次:
var
RegExpr:TRegExpr;
Input:string;
begin
RegExpr:=TRegExpr.Create;
Input:=memo1.Text;
try
RegExpr.Expression :='<option value="(\d{3})" >(.*?)</option>';
RegExpr.Exec(Input);
repeat
...
until not RegExpr.ExecNext;
finally
FreeAndNil(RegExpr);
end;
2.XML解析
类:MSXML2_TLB.pas
引用:MSXML2_TLB
使用XPATH解析XML,迭代每个节点:
var
ResponseDomXml : TDOMDocument30;
ResponseDomNodeList:IXMLDOMNodeList;
strName:string;
begin
ResponseDomXml := TDOMDocument30.Create(nil);
ResponseDomXml.DefaultInterface.async := False;
ResponseDomXml.DefaultInterface.setProperty('SelectionLanguage', 'XPath');
ResponseDomXml.DefaultInterface.loadXML(memo1.text);
ResponseDomNodeList:=ResponseDomXml.DefaultInterface.documentElement
.selectNodes('//FieldInfo');
for i := 0 to ResponseDomNodeList.length-1 do
begin
strName:= ResponseDomNodeList[i].selectSingleNode('FieldName').text;
...
end;
ResponseDomXml.Free;
end;
TDOMDocument30通过DefaultInterface接口对XML访问,进而用selectNodes和selectSingleNode查询指定的节点。
3.图像显示(ImageEn)
将控件拖到窗体,除了可以设置各种属性之外,它可以很方便加载不同类型的图片,包括本地的和远程的。主要使用IO接口:
ImageEnView1.IO.LoadFromFile(sFileName);
ImageEnView1.IO.LoadFromURL(sUrl);
更多的访问接口请查看TImageEnIO。
4.表单显示(DevExpress)
这套控件非常强大,当然使用起来也不容易。每个控件基本有普通和数据库两种版本,普通的就手动指定值,数据库的就绑定到数据源的字段。需要认真学习这套控件的使用。