面向对像
如下是面向对像的专题
---------------------
不少的程序员,编写软件多年,大部份的时间从事数据库软件的开发工作,很少使用面像对像,不知道什么是面像对像,面像对像是做什么用的,为什么要面像对像,本专题介绍面像对像的来拢去脉,及面向对像这个软件编写思想(方法)的应用.
_______________________
讲解的过程如下(一步一步提高)
①简单数据类型->②子界->枚举->集合->一维二维数组->记录类型->③方法(函数,过程)->④类
->⑤组件编写->⑥接口->⑦COM(COM+)->⑧自动化
_________________________________________________
一,简单数据类型,这个最简单,使用也最多
如:
Var I: integer
Const I: integer = 12
{加入各种数据类型的比较}
--------------------------------
二,自定义数据类型
1,如何定义枚举,怎么用?
---------------
方法1
type
Suit=(Spades,Heartes,Diamonds,Clubs);
var
a:Suit;
-----------------
方法2
var
a: (Spades,Heartes,Diamonds,Clubs);
-----------------
注意:
枚举是不可以直接取其中某一个元素的;
枚举元素不可以是字串或实数;
枚举类型属于顺序类型,枚举类型中的第一个元素无前趋,最后一个元素无后继;
枚举变量的值只能用赋值语句来获得
for a := Spades to Clubs do
begin
//判断某个元是否在枚举里面
if a = Heartes then ShowMessage('Spades');
end;
我非得要取其中某一个元素,怎么办???
----------------------------
定义一个枚举型的一维数组常量,其元素个数与枚举相同,然后取一维数组中的元素就行
如:
procedure TForm1.Button1Click(Sender: TObject);
type
a=(Spades,Heartes,Diamonds,Clubs);
const
b: array[a] of string =('Spades','Herates','Diamonds','Clubs');
var
S: string;
aa: a;
begin
for aa := Spades to Clubs do
begin
S:=S+' '+b[aa];
end;
ShowMessage(s);
end;
-----------------------------------
这样做不是多此一举,还不如直接用字串型一维数组!不是这样子的
如:有red yellow blue white black 五种色,从中取三种,随意排列,不相同的有多少种,
这样的算法,就要用到枚举及枚举数组
-------------------------------------
2,子界(子界怎么定义?怎么用?为什么要有这个数据类型?)
当我们定义 i: integer时,i的取值是(-2147483647-2147483648),当我们定义 i:Word时i的取值是(0..255)
如果我们要定认一个i取值只能是0-10怎么办?用子界!
子界的上界必须大于下界,子界只能是整型,字符,枚举中的元素;
子界类型,在编译期会判定赋值数据的范围 如:
procedure TForm1.Button1Click(Sender: TObject);
var
a : 1..10;
begin
a := 20;//不被编译通过
end;
----------
a := StrToInt(Edit1.Text);//当输入的值>10是有效的,因为这是在运行期
子界类在Case of else end语句中常使用
------------------------------------------
3,集合(为什么要用集用,集合怎么定义)
集合的数据元素是无序的所以它不可以For 变量 := 元素 to 元素,枚举中的元素是有序的,所以它可以用(For 变量 := 元素 to 元素)
枚举中的元素不可以,减掉一个元素产生新枚举,而
集合确可以减掉一个元素,产生新集合
---------------------------------------
type 集合类型标识符=set of 基类型
type a = Set of Byte;集合最大元素个数为255
集合可以与枚举配合定义来判定枚举中的元素,也可以与子界配合定义来确定集合元素的个数
-----------------------------------
var
a : set of 1..7;
begin
a:=[1,2,3,4];
end;
集合中必须确定元素个数
----------------------------
与枚举配合
type
a = (One,Two,Three);
var
aa: set of a;
------------------------
集合中没有常量的概念,要使用集合常量直接合用就行了
如:
procedure TForm1.Button1Click(Sender: TObject);
var
i:Integer;
begin
i := StrToInt(Edit1.Text);
if i in [1..10] then ShowMessage('Yes') else ShowMessage('NO');
end;
--------------------------
又如,限定Edit中的输入
procedure TForm1.Edit1KeyPress(Sender: TObject; var Key: Char);
begin
if not (key in ['0'..'9',#8,#13,'.']) then Key := #0;
end;
--------------------------------
集合中的元素是不可以用字串的形式显示出来的!
-------------------------------
3,数组(为什么要用数组?用数组有什么好处?怎么用?)
当要定义(A1..A100)100个变量时,要么一个一个定义,要么用数组去定义,数组还可以定义表格式数据(二维数组)
什么叫数组?->就是一组数据,没必要理解的那么复杂
①一维数组
一维数组的定义
var A: array[1..10] of integer;
一组开数组的定义
var A: array of integer;
一维开数组的下标都是从0开始的自然数,
用SetLenth(A,5)->可以规定一个一维开数组的长度
动态->真正的动态是不可以用的->动态的东西只有确定下来成为静态才能使用.
Low(A)->一维数组(包括一维开数组)的的低位下标
High(A)->一维数组(包括一维开数组)的高位下标
数组的下标必须是整数吗?->不是的->有序数据类型都可以做数组下标,如用枚举中的元素也可以做数组的下标
type
A= (One,Two,Three);
var
B: array[A] of integer;
------------------------------------
②二维数组
二维数组是定义表格数据的->表格中的每个格子都是一个变量,不可每一行数据是分组了的,
如定义可以装5X5个变量的表格数据
var
A: array[1..5] of array[1..6] of integer;
行数 列数
二维开数据的定义如下:
var
A: array of array of integer;
这是定义一个行数不定,列数不定的表格数据->表格数据不要与数据库中的表搞混了,表格数据只能放一种类型的数据,而表什么数据类型都可以放
二维动态数组的确定行列的方法->SetLenth(A,5,5);
注意:
二维数组(包括动态二维数组)中
Low(A)->二维数组中第一维(列)的低位下标
High(A)->二维数组中第一维(列)的高位下标
Low(A[0])->二维数组中第二维(行)的低位下标
High(A[0])->二维数组中第二维(行)的高位下标
二维数组中->数据格的定位方法是->列数*行值+列
例如:显示5行6列的一个表格数据,表格中的格子填上0..29
procedure TForm1.Button1Click(Sender: TObject);
var
a: array of array of Integer;
I: Integer;
J: Integer;
S: string;
begin
SetLength(a,5,6);
// 行,列
for I := Low(a) to High(a) do begin
for J := Low(A[0]) to High(A[0]) do begin
A[I,J] := 6*I+J;
// 列数*行值+列
S := S + IntToStr(A[I,J]);
end;
S := S + #13;
end;
Label1.Caption := S;
end;
//-------------------------------------
4,比数组更高一层的数据类型就是记录类型
为什么要作记录类型?记录类型有什么用?怎么用?
二维数组是一个表格数据,不过这个表格数据中只能放一种类型的数据,他有行,也有列
而记录类型与二维数组有点相同,他是定义列数据的,他只能有一行,他可以使用不同类型
的数据,如果要多行,就要用记录类型指针或与一维数据配合,做到这样,就已经与数据库的
表很接近了,记录类型在工程定用当中,可以用于保存用户名称,用户密码这样的数据,要用
到用户名称,用户密码或是用户权限或是用户角色的时候,叫用这个记录类型就可以了.这就
是记录类型的基本意义了
-----------------------------------------
怎么定义一个记录类型?
记录类型数据,已经很接近面向对像方法中的类了,其实他就是类,只不过没有方法与属性,不能
继承,封装而已,它不需要实列化就可以使用,他的定义方法与类的定义方法是一样的
定义如下:
Type
T记录类型数据名称=Record
字段表
end;
注意:定义类类型数据的时候,不要在类类型属性后面加(;)号,在这里不要在record后面加;号
下面是个列子
-----------------
procedure TForm1.Button2Click(Sender: TObject);
type
TA = record
a : Integer;
b : string;
end;
var
A: TA;//记录类型变量
const
B: TA=(a:12;b:'HaHa'); // 记录类型常量,字段名:值;字段名:值 注意:给字段赋值时,中间用分号隔开
begin
A.a := 12;
A.b := 'HaHa';//给记录类型变量赋值
ShowMessage(B.b);
end;
----------------------------
type TA = record end; 与type TA = packed record的区别,一个是非压缩记录类型,一个是压缩记录类型
压缩记录类型虽然在节约空上有优式,但是因不同的CPU数据压缩的位置也不一样,
嵌套记录类型的写法
type//但是只要一个type就行
TA=record
a:integer;
end;//记录类型定义完毕
TB=record
b:integer;
c:TA;//嵌套
end;//记录类型定义完毕
----------------------------------------------
嵌套记录类型的常量写法与不嵌套是一样的,记录的嵌套,不过是增加记录的字段而已
Const
A : TB=(B:12 ; C:(a:13));
自已的字段 分隔号 嵌套字段
----------------------------------------------
当使用控件属性时,常用with限定语句,如
-----------------------------
with Memo1 do begin
Lines.add('HAHA');
end;
------------------------------
如果是控件内属性中的方法,要这样
-------------------------------
with Memo1,Lines do begin
Add('HAHA');
end;
----------------------------
注意Memo1,Lines这里的,号相当于平时用的.号
其实上面也可以用.号的但意义不一样
Memo1.lines->只能直接用它的lines属性
Memo1,Lines->可以直接用Memo1的属性,同时也可以用Memo1.lines中的属性
建议写成Memo1,Line(前面一个用于确定基对像的)
--------------------------------------------
记录类型与一组数组的配合
procedure TForm1.Button5Click(Sender: TObject);
type
TA = record
Name : string;
Age : Integer;
end;
var
A:array[1..5] of TA;
I: Integer;
begin
//--------------写记录数组---------------
for I := Low(A) to High(A) do begin
A[i].Name := 'HAHA';
A[i].Age := 12;
end;
//-----------读记录数组--------------------
for I := Low(A) to High(A) do begin
Memo1.Lines.Add(A[i].Name + ' '+IntToStr(A[i].Age));
Memo1.Lines.Add('------------')
end;
end;
---------------------------------------------------------
5,函数与过程
函数与过程统称方法,如果一个记录类型加入方法,他就更像类了,下面是方法的内容
方法分类
①系统自定义的函数与过程->大量的Delphi函数->大量的API函数->这些都叫标准方法
②各种给件提供的方法,如双击,单击,鼠标动作,属性更改等等
③用户自定义方法
---------------------
关于方法的参数,
函数只有一个返回值吗?过程没有反回值吗?过程与函数的区别在哪里?
1,函数的返回值不只一个,可以有很多过个,有多少叁数就可以有多少个返回值
2,过程的返回值与过程的叁数个数相中,
函数必须要一个返值,不论你返回什么,过程可以不要,
3,函数的返回值中有一个默认的变量->Result->在函数体内,它自动把函数名映射成为自已
Result是个隐式的局部变量,这个功能也可以通过编译开关把它关掉,当在Project->Options->Compiler
中禁用了编译器的Extended Syntax,或使用{$ExtendedSyntax Off}或{$X-}指令,那么就不能使用隐局
部变量Result.
4,方法中的叁数,全部以(;)号隔开,不带叁数类型申明的叁数,默认为值叁(Const 叁数),
方法叁数可以使用默认值,写法与全局变量一样直接在叁数类型后面写上(=)号,如:
function(a: integer; b: integer = 12);
5,Var 叁与 Out 叁,使用var叁或Out叁,表明,这个叁数是地址传送的,而不是接值传送的,调用方法所使用的
叁数,必须是变量,当方法内Var叁或Out叁值改变时,调用中传入的变量也也同时改变,但是,var叁与Out叁是有
区别的,var叁接收传入数据,也负责传出数据,一般而言,Out叁是不接收数据的,他主要用于输出数据,它在方
法体内,并不叁与运算,如果叁与运算Out叁与Var叁就什么区别,Out叁是用引用来传递的,它告诉方法的输出在
什么地方.
-----------------------
function TAA(a,b: Integer; out c: Integer):Integer;
begin
Result := a + b ;
c := a*b;
end;
procedure TForm1.Button3Click(Sender: TObject);
var
a,b:Integer;
begin
b:=TAA(12,13,a);
ShowMessage(IntToStr(a));
ShowMessage(IntToStr(b));
end;
------------------------------
方法的摆放位置
1,只接放在单元里,这样方法名前面就不要加任何的方法归属
2,做为窗体方法,要在窗体类定义时,申明这个方法,方法前面要加上方法属主,控件方法基本上都是窗体方法
如:方法实现部份
procedure TForm1.Button1Click(Sender: TObject);
begin
end;
-----------------
方法引用申明部份
type
TForm1 = class(TForm)
Button1: TButton;
Memo1: TMemo;
Label1: TLabel;
Button2: TButton;
Button3: TButton;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
-------------------------------
3,方法做为接口方法
方法引用要申明在单元接品口当中
如:方法实现部份
procedure AAA;
begin
ShowMessage('AAA');
end;
------------------
单元接口中申明部份
procedure AAA;
var
Form1: TForm1;
implementation
--------------------------
方法如果要提供给其它单元使用,方法必须在接口内申明,无论申明在单元接口内,还是窗体方法内,
其实窗体方法申明也是在接口中,单元接口的作用说是,引入外部单元,方法(如导入DLL中的方法)
导出本单元方法,类的地方
----------------------------
引用其它单元的窗体方法,打上窗体名就叫得出,
如果是使用其它单元的单元方法,要打上其它单元的单元名称才能叫得出
------------------------------------
如果没有在接口中申明方法(包括窗体申明),那只有在方实现部体后面的代码才能使用,前面是不可以使用的
---------------------------------------
方法的类型
方法是分种类的,这不是按功能来分类的,方法的功能是编写者要让方法去处理千奇百怪的问题,方法的类型是接方法的使用来分的,方法分为这几
种类型
①静态方法(Static)->所有不加特殊说明的方法,都是静态方法->这是方法的默认类型->什么叫静态方法?->EXE运行时把方法名压入窗体栈,单元栈
执行线程,直接叫用内存栈名来执行方法代码.
②重载方法(OverLoad)->只有同名方法提供不同功能才有方法重载,必须给重载方法传入不同的叁数,不可以二个一模一样的方法用重载,否则编译
器无法识别->编译器把重载方法的方法名压入窗体栈,单元栈,或事件栈中,同时维护一个重载方法表,根据调用叁数去匹配执行哪个方法的的代码
如:
function A(i: integer): integer; OverLoad;
begin
Result :=i;
end;
------------------
function A(i: Double): Double; OverLoad;
begin
Result :=i;
end;
-------------------------
当传入整数时返回整数,当传入小数时返回小数
当方法做为单元方法或事件方法时,只有上面这二种情况,当方法做为类的方法时,情况就比较多,下面的方法是类中方法的类型,而类中的方法类型
决定这个类的属性,如只含有虚拟方法的类->虚拟类(Virtual),只含有抽像方法的类->抽像类(Abstract),
③虚拟方法->编译器在类中维护一个虚拟方法表(VMT=Virtual Method Table),所有的虚拟方法名称(包括Ancestro与Child)都放入这个表中,把VDT表
中的方法名称压入类栈中,但是并不确定方法的代码地址,在执行泒生类方法或父方法时,才确定虚拟方法的执行的地址.是执行Ancestor中的方法
还是Child中的方法
如:
-------------------
implementation
{$R *.dfm}
type //定义Ancestor类
TA = class
procedure A; virtual;//虽然它是Virual方法,但也要提供实现,
end;
//-------------------------------
type TAA = class(TA)//定义chid类
procedure A;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
B: TA;
BB: TAA;
begin
// B := TA.Create;
// B.A;//调用Ancestor方法
BB := TAA.Create;
BB.A;//调用Child方法
end;
{ TA }
procedure TA.A;
begin
ShowMessage('Ancestor');
end;
{ TAA }
procedure TAA.A;
begin
// inherited;//如果调用继承,那么它就会执行Ancestor中的方法后再执行Child方法
ShowMessage('Child');
end;
---------------------
Ancestor中的方法指明为Virtual有什么好处呢?不指明行不行?其实上面的方法,Ancestor类中的方法不指明为Virtual效果是一样的
但是指明为Virtual有一个好处,一个对像的VMT表中,不仅包含自己申明的虚拟方法地址,而且也包含它的所有覆盖地址,也基于此
VMT表可以根据类中的调用,快速定位是执行Ancestor中的方法还是Child中的方法
在类中,不要把Ancestor类与Child类独立分开看,(Ancestor->Child)它们是个整体.就像二维数据中,不可以把前一维与后一维折开来看一样!
其实二维数据的本质就是二个一维数给关联,而Ancestor->Child是二个类的关联,在本质上它们是一样的.后一个中的数据很多东西是由前
一个决定的.
④Dynamic->动态方法->动态方法与虚拟方法基本相同,不同的是,编译器为类自动维护一个动态方法表(DMT),在DMT表中放的不是方法的名称
而是一个值,通过这个值去查找方法名称,他的速度要比Virtual方法慢,但是占用内存小些.
⑤Override->覆盖方法->允许泒生类写一个新功能的方法,代替父类的方法.这个方法属性是应用在Child类方法中的,仅能覆盖Ancestor类的Virtual
方法与Dynamtic方法,它不是Ancestor方法
------------------
implementation
{$R *.dfm}
type TYoung = class //定义Ancestor类(年轻人类)
private
Name: string; //定义姓名,性别,年龄变量
Sex: Char;
Age: Integer;
public
constructor Create(nam: string; s: Char; a: Integer);//当构造(衯使化类时要求传入三个叁数)
function GetInfo: string;//通过传入的叁数给类变量赋值
procedure Display; virtual;//显示信息,这是虑拟方法,会跟据传入的叁数来调用使用哪个Child的方法
end;
//--------------------------------------
type TStudend = class(TYoung)
private
Num: Integer;
Section: string;
public
constructor Create(nam: string; s: Char; a: Integer; n: Integer; sec: string);
procedure Display; override;
end;
//--------------------------------------
type TWorker = class(TYoung)
private
Salary: Integer;
Factory: string;
constructor Create(nam: string; s: Char; a: Integer; sal: Integer; fac: string);
procedure Display; override;
end;
{ TYoung }
constructor TYoung.Create(nam: string; s: Char; a: Integer);
begin
Name := nam;
Sex := s;
Age := a;
end;
procedure TYoung.Display;
begin
ShowMessage(GetInfo);
end;
function TYoung.GetInfo: string;
begin
GetInfo := '姓名:' + name + #13 + '性别:' + Sex + #13 + '年龄:' + IntToStr(Age);
end;
{ TStudend }
constructor TStudend.Create(nam: string; s: Char; a, n: Integer; sec: string);
begin
inherited Create(nam, s, a);
Num := n;
Section := sec;
end;
procedure TStudend.Display;
begin
ShowMessage(inherited GetInfo + #13 + '学号:' + IntToStr(Num) + #13 + '班级:' + Section);
end;
{ TWorker }
constructor TWorker.Create(nam: string; s: Char; a, sal: Integer; fac: string);
begin
inherited Create(nam, s, a);
Salary := sal;
Factory := fac;
end;
procedure TWorker.Display;
begin
ShowMessage(inherited GetInfo + '工资:' + IntToStr(Salary) + '工厂:' + Factory);
end;
//-----------------------------------------------
procedure TForm1.Button1Click(Sender: TObject);
var
Young: TYoung;
begin
Young := TStudend.Create('张三', 'M', 12, 3, '五年级');
Young.Display;
end;
------再举一相面的例子------------
type TA = class
procedure B; dynamic;
end;
type TB =class(TA)
procedure B; override;
end;
{ TA }
procedure TA.B;
begin
ShowMessage('Ancestor');
end;
{ TB }
procedure TB.B;
begin
ShowMessage('Child');
end;
//----------------------------------
procedure TForm1.Button2Click(Sender: TObject);
var
C: TA;
begin
C := TB.Create;
C.B;{Child}
end;
------------------------------------------
⑥(Abstract)抽像方法->抽像方法只能在Virtual方法或Dynamic方法后,再申明此二种方法为Abstract方法,其它的方法是不行的,类方法中没有
OverLoad方法,OverLoad方法,仅限于窗体方法与单元方法使用,把方法申明为抽像方法有个好处,Ancestror类不用去实现这个方法,让Child类
去实现,如:
type TA = class
procedure A; virtual;abstract;//包含这种抽像方法的类,就叫它抽像类
end;
抽像方法->Child类必须实现吗?不一定,如果是创建Child类的实列,而不使用Ancestor类中的方法,可以不实现,如果要使用Ancestor中的方法,
就必须实现,这样可以用Child的实列执行此方法,但不可以用Ancestor的实例去执行此方法
⑦Class procedure ,Class function 类方法,类方法与类中的一般方法不一样,类方法的属主是类,类中的一般方法,属主是类实例,类方法可以不实例
化,直接用类引用,而类的一般方法则不行,
type TA = class
procedure A; virtual;abstract;
class procedure AA;virtual;
end;
-----------------------
procedure TForm1.Button2Click(Sender: TObject);
begin
TA.AA;
end;
6,类与对像
什么是类?类怎么定义?类怎么用?为什么要用类?什么是对像?对像有什么用?
1,对像与类不要搞混的,对像是种概念性的东西,窗体是对像,所有的控件都是对像,那变量是不是对像呢?对某种
程度上而言,一个变量也是一个对像(数据类型变量),所有的过程与函数都是对像(它的实例),在delphi中,对像只
有一个东西才叫对像类(TObject)
那么procedure TForm1.Button1Click(Sender: TObject);中Sender: TObject是什么意思呢???Sender代表应用程序的
执行线程地址,意思是,让应用程序的执行线程执行Button1的单击代码.(向执行线程传入Button1单击过程这个对像)
2,类->它也是对像(类的实例),->它是一种用户自定义的数据类型->由类字段(内部数据)->类方法(类对像方法)组成(封装而成);
-------------------
3,类怎么定义
type
TA = class
a: Integer;
b: string;
end;
------------------
注意:类只能定义在单元内(包括窗体接口),不可以定义在方法内部,(当然包括控件方法,自定义方法)
这样定义是有错的,类一旦定义,他就有一个构造过程Create(为类实例分配内存空间)和一个析构过程Free(释放实例内存空间)
delphi不可以定义一个空类,type end;中间什么都没有,但可以有空语句块begin end;中间可以什么都没有,为什么会这样???
那是因为用户自定义的数据类型,在程序的运行期是加载到EXE的全局内存中(栈Stack)中,而栈对一个EXE来说是他是永久性的,使用的
是物理内存,非常珍贵,所认不许可空类型,而事件方法,或窗体中的方法,它使用的是EXE的(堆Heap),堆内存是个可大可小,可有可无的
特性,用时申请内存,不用时放掉内存.所以他不可以定义在方法内.
procedure TForm1.Button1Click(Sender: TObject);
type
TA = class
a: Integer;
b: string;
end;
begin
end;
---------------------------
窗体类
------------
type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
//--这是窗体类--
type
TA = class
a: Integer;
b: string;
end;
//---定义结束
end;
var
Form1: TForm1;
-------------------------
单元类
-------------------------
implementation
{$R *.dfm}
//--这是单元类--
type
TA = class
a: Integer;
b: string;
end;
//---定义结束
---------------------
单元接口类
-----------------
type
TForm1 = class(TForm)
Button1: TButton;
private
{ Private declarations }
public
{ Public declarations }
end;
//--这是单元接口类--
type
TA = class
a: Integer;
b: string;
end;
//---定义结束
var
Form1: TForm1;
--------------------------------------------
如果类只是这样那在应用上与记录类没什么区别,记录类型还可以直接使用,
而自定义类还要实例化才能使用
-------------------------------
类方法与一般方法
//--这是单元接口类--
type
TA = class
a: Integer;
b: string;
class procedure ShowMe;//类方法,类方法与一般的方法没有什么太多的区别,它可以处理类的初使化与结束化
procedure ShowShe;//一般方法
end;
//---定义结束
--------------------------
下面是一个类方法的使用
-----------------------
类方法(Class methods)是一类特殊的方法,它们在声明时要以 class 开头:
type
TFigure = class
public
...
class procedure GetInfo(var Info: TFigureInfo); virtual;
...
end;
实现时也以 class 开头:
class procedure TFigure.GetInfo(var Info: TFigureInfo);
begin
...
end;
乍一看好象平时没有遇到过这个东东,好象这个东东也没有什么大作用,其实不然。比如我们有时为输入密码或其他常用数据专门做一个 Form,但由于其代码都在 Form 定义的 unit 里面,所以在使用时仅仅需要几行代码,比如:
with TfrmPassword.Create(nil) do
try
ShowModal;
finally
Free;
end;
虽然这样的代码已经很简洁,但如果写多了还是很讨厌的。利用类方法可以使其更简洁:
TfrmPassword = class(TForm)
...
public {Public declarations }
class function Execute: TModalResult;
end;
...
class function TfrmPassword.Execute: TModalResult;
begin
with TfrmPassword.Create(nil) do
try
Result :=ShowModal;
finally
Release; //注意此处必须为release不能为free!
end;
end;
然后只用一行 TfrmPassword.Execute; 即可直接完成调用!
------------------------
类属性
---------------------------
类属性是专门用于给类变量(也称类字段)赋值的特殊过程
类属性的写法如下:属性一般都放在类的发布域里面
-----------------
type
TA = class
i: Integer;//类变量主要是提供给类方法使用的
published
property F : Integer read i write i;
// 属性名称 属性类型 读类变量 写类变量
end;
------------这里改变F的值就改变了I的值----------------------------------------------------
为什么要使用类的属性??
这主要是在类的继承上,因为有些类变量是保护的,就是继承类不可以看到,继承类看不到祖先类的保护变量,但又要改变祖先类变量的
值以执行祖先类方法,怎么办?那就只能用类的属性方法,也基于这一点,类属性方法一般都写在publish域里的原因
--------------------------------
类的属性类型有很多,几乎所有基类都可以做为类属性的数据类型,有数组,枚举,实型,集合,字串,还有对像(这在开发控件中有详细说明).
------------------------------------
类有一个特殊的过程,只能是过程
Constructor Create(过程名称) Constructor 后面记住不要写procedure
当然也有析构过程,Free与Distructor,Free是释放类的内存,Destructor是在释放类的内存前做一些事情
这个过程,可以在类的实列化时做一些事情
Ancestor类与Child类的关系,他样是不断扩展功能的关系,与接口不一样,接口单元,只是把类方法输出的单元
方法名称是做什么用的?为什么要写方法名称?
---------------------------------------------------------
Delphi中使用方法指针,实现方法,方法名称与变量名称是一样的,它标识方法在内存中的名称,执行时,主线程依据这个内存名称,来执行方法代码,
类名也是一样的道理,窗体名称,单元名称也是一样的道理,为了唯一标识,一个工程中(EXE)中,不可以有相关的单元名,也不可以有相同的窗体名
因为这二样的属主直接就是EXE.
-------------------
类的运算符(IS, AS, =,<>)
---------------------
①(=)用于判断二类的祖先是否相同
procedure TForm1.Button1Click(Sender: TObject);
begin
if Edit1.ClassType=Edit2.ClassType then ShowMessage('Ancestor类相同');
end;
--------------------------------
②(<>)用于判断二个类的祖先是否不同
③(IS)用于判断一个实例(对像)是否是另一个类的实例或是另一个类的子类
-----------------------
procedure TForm1.Button2Click(Sender: TObject);
var
i: Integer;
begin
for i := 0 to (ControlCount- 1) do begin
if Controls[i] is TEdit then Controls[i].Enabled := False;
//Controls仅指可视控件,Component组件,包括可视与不可视
end;
end;
------------------------
④(AS)用于将对像(实例)转成自已的类或父类,如果转成功返回True否则False
------------------
procedure TForm1.Button2Click(Sender: TObject);
var
i: Integer;
begin
for i := 0 to (ControlCount- 1) do begin
if Controls[i] is TEdit then begin;
(Controls[i] as TEdit).Font.Color := clRed;
end;
//Controls仅指可视控件,Component组件,包括可视与不可视
end;
end;
procedure TForm1.Label1MouseEnter(Sender: TObject);
begin
(Sender as TLabel).Color := clRed;
end;
--------------------
------------------------
7,组件编写
------------------------
Delphi2007编写组件的方法
1,创建一个包
File->New->Package->(给包重命名)
2,Component->New VCL Component->确定(Ancestor)->把组件单元加入包中
-----------------------------------------------------------------
如何做窗体组件并同时注册二个组件?
----------------------------------
做窗体组件用Frame->Frame的用法与窗体一样,几乎一般窗体上可以放的东西,他都可以放
同时注册二个组件时,请在第一个给件单元中引用第二个组件,所有的注册只能写在第一个组件单元:
代码如下:(仅为叁考)
-------------
procedure Register;
implementation
uses U_MyFram;
procedure Register;
begin
RegisterComponents('Huang', [TMyFirstCom]);
RegisterComponents('Huang', [TMyFrame]);
end;
--------------------
如何为组件加入图标呢?
1,Image Edit->New->Component Resource File->Contents->New->BitMap->24X24(建立一个组件资源图标,此工具D7中有提供)
注意:
①位图名与组件类名完全相同并且全部大写,如果有多个组件也同样,为每一个组件类做一个位图
②DCR文件名与第一个控件类的单元文件名完全相同
③包文件(BPL->右键->View Source)->在编译命令组中加入{$R DCR文件名.dcr} 如
----------------
package MyPackage;
{$R *.res}
{$R 'MyComponent.dcr'}//这是加入的
{$ALIGN 8}
{$ASSERTIONS ON}
{$BOOLEVAL OFF}
{$DEBUGINFO ON}
{$EXTENDEDSYNTAX ON}
{$IMPORTEDDATA ON}
{$IOCHECKS ON}
{$LOCALSYMBOLS ON}
{$LONGSTRINGS ON}
{$OPENSTRINGS ON}
{$OPTIMIZATION ON}
{$OVERFLOWCHECKS OFF}
{$RANGECHECKS OFF}
{$REFERENCEINFO ON}
{$SAFEDIVIDE OFF}
{$STACKFRAMES OFF}
{$TYPEDADDRESS OFF}
{$VARSTRINGCHECKS ON}
{$WRITEABLECONST OFF}
{$MINENUMSIZE 1}
{$IMAGEBASE $400000}
{$IMPLICITBUILD ON}
requires
rtl,
vcl;
contains
MyComponent in 'MyComponent.pas',
MyFrame in 'MyFrame.pas' {Frame1: TFrame};
end.
---------------------------
组件的属性如何写?
其实组件与类并什么多大区别,因为组件的所有功能都要依靠类来实现
----------------------------
类属性与类变量有什么区别?为什么有了类变量还要提供类属性呢?
①当我们给一个类变量赋值时,我们的方法是给类实例赋值,类中的值并没有改变,当Ancestor类把类变量定义到保护区的strict Private(严格保护)
时,Child类是无法看见过个值的,(无法看见,并不等于没有,Child还是有这个类变量的,只不过它放在Child的inherited区里,不可读,也不可写),这时child
类又要改变这个值时,只能通过Ancestor类提供的属性方法进行存储.
如:
--------------
type TA = class
strict private
FIntPro: Integer;
published
property IntPro: Integer read FIntPro write FIntpro;//其实只要写到属性数据类型完就可以Ctrl+Shift+C,后面的系统自动补齐,就如写完begin->
//按住SHIft+回车系统自动给你加上end一样.
end;
---------------
②类的事件?组件可能有事件,组件自己都是由类抽像出来的,类当然可以有事件
事件是什么?
1,事件是一种特殊的方法,事件名称就是事件方法的内存地址名称,
2,事件是属性,类中采用属性的形式,实现事件,事件属性,不使用Read与Write部份,
3,事件方法只能是过程,因为一个空函数返回一个不确定的结果,所以函数类型的空事件处理器可能是非法的.但可能用过程的var叁来实现
事件的返回值 如(控件的KeyPress与KeyDown事件,返回的就是key叁变量),自定义事件如
在单元区内:
type TOnKeyDown = procedure(Sender: TObject) of object;//of Object标明这个过程传入的是一个对像
事件属性
property OnKeyDown: TOnKeyDown read FOnKeyDown write SetOnKeyDown;
8,(类接口)与DLL