Delphi(程序人生)

Delphi Programer DoubleCat

导航

面向对像

如下是面向对像的专题
---------------------
不少的程序员,编写软件多年,大部份的时间从事数据库软件的开发工作,很少使用面像对像,不知道什么是面像对像,面像对像是做什么用的,为什么要面像对像,本专题介绍面像对像的来拢去脉,及面向对像这个软件编写思想(方法)的应用.
_______________________
讲解的过程如下(一步一步提高)
①简单数据类型->②子界->枚举->集合->一维二维数组->记录类型->③方法(函数,过程)->④类
->⑤组件编写->⑥接口->⑦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


 

 

 

 

 

 

 

 

 

 

 


 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


  

 

 

 

 


 

posted on 2008-06-17 15:31  DoubleCat  阅读(515)  评论(0编辑  收藏  举报