程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 更多編程語言 >> Delphi >> 理解Delphi的類(十) - 深入方法

理解Delphi的類(十) - 深入方法

編輯:Delphi

類中包含字段、方法和屬性(屬性包含事件); 字段是靠方法組織與操作的; 屬性也只 是方便和規范了字段與方法的使用.

因此我覺得: 方法是最重要的.

方法無處不在, 它不僅僅存在與類中. 先從非類中的方法談起吧, 因為類中的方法也 擁有全部這些特性.

離開類的懷抱, 我們更喜歡把方法叫做過程或函數.

//要點1: 過程用 procedure 定義, 函數用 function 定義; 過程沒有返回值, 函數 需要返回值.

procedure MyProc;
begin
  ShowMessage('ok');
end;
function MyFun: string;
begin
  Result := 'ok';
end;

//要點2: 過程和函數都可以有一個或多個參數; 參數用 ; 號分割

procedure MyProc(i: Integer);
begin
  ShowMessage(IntToStr(i));
end;
function MyFun(x: Integer; y: Integer): Integer;
begin
  Result := x + y;
end;

//要點3: 在調用時, 參數使用 , 分割的

function MyFun(x: Integer; y: Integer): Integer;
begin
  Result := x + y;
end;
{調用}
procedure TForm1.Button1Click(Sender: TObject);
var
  i: Integer;
begin
  i := MyFun(1,2);
end;

//要點4: 多個相同類型的參數可以簡化寫法

function MyFun(str: string; x,y,z: Integer): string;
begin
  Result := str + IntToStr(x + y + z);
end;

//要點5: 函數的返回值可以使用 Result , 也可以使用函數名(但不提倡)

function MyFun1(x,y: Integer): Integer;
begin
  Result := x + y;
end;
function MyFun2(x,y: Integer): Integer;
begin
  MyFun2 := x + y;
end;

//要點6: Result 可以參與運算, "函數名"不可以

function MyFun(x,y: Integer): Integer;
begin
  Result := x + y;
  Result := Result * 2;
end;

//要點7: 不僅如此, Result 還有更靈活的運用

function MyFun(b: Byte): Char;
begin
//Result := Char(b); {我們當然可以這樣寫}
  Byte(Result) := b;  {這樣也行}
end;
{System 中就有這樣一個函數}
function TObject.ClassType: TClass;
begin
  Pointer(Result) := PPointer(Self)^;
end;

//要點8: 忘了寫返回值的函數, 也可以當過程用(沒有人會這樣做, 但 Delphi 竟然 也允許)

function MyFun(var x: Integer): string;
begin
  x := x + 1;
end;
{調用}
procedure TForm1.Button1Click(Sender: TObject);
var
  i: Integer;
begin
  i := 2;
  MyFun(i);
  ShowMessage(IntToStr(i)); {3}
end;

//要點9: 沒有參數的過程或函數, 在調用時可以省略 (); 也可以帶著

function MyFun: string;
begin
  Result := 'ok';
end;
{調用}
procedure TForm1.Button1Click(Sender: TObject);
var
  s: string;
begin
  s := MyFun;
  ShowMessage(s); {ok}
  s := MyFun();
  ShowMessage(s); {ok}
end;

//要點10: 過程和函數都可以有一個或多個默認參數; 但必須在非默認參數後面

function MyFun(s1: string; s2: string='好人'): string;
begin
  Result := s1 + s2;
end;
{調用}
procedure TForm1.Button1Click(Sender: TObject);
var
  str1,str2: string;
begin
  str1 := '萬一';
  str2 := '壞蛋';
  ShowMessage(MyFun(str1));   {萬一好人}
  ShowMessage(MyFun(str1,str2)); {萬一壞蛋}
end;

//要點11: 參數可以分為: 默認參數(傳值)、var(傳址)、out(輸出)、const(常數)四 類

{默認參數是傳值, 不會被改變}
function MyF1(x: Integer): Integer;
begin
  Inc(x);
  Result := x;
end;
{var參數是傳址, 會被改變}
function MyF2(var x: Integer): Integer;
begin
  Inc(x);
  Result := x;
end;
{out參數是為支持Com的, 和 var 的結果是一樣的, 一般我們用不著它}
function MyF3(out x: Integer): Integer;
begin
  Inc(x);
  Result := x;
end;
{const參數是絕對不可以賦值的, 這是被編譯器優化的方式, 盡量多用}
function MyF4(const x: Integer): Integer;
begin
//Inc(x); {這句會出錯, 因為帶 const 前綴的參數是不可以更改的}
  Result := x;
end;
  
//調用測試
procedure TForm1.Button1Click(Sender: TObject);
var
  a: Integer;
begin
  a := 6; MyF1(a);
  ShowMessage(IntToStr(a)); //6
  a := 6; MyF2(a);
  ShowMessage(IntToStr(a)); //7
  a := 6; MyF3(a);
  ShowMessage(IntToStr(a)); //7
  a := 6; MyF4(a);
  ShowMessage(IntToStr(a)); //6
end;

//要點12: implementation 區中的過程或函數, 只能在本單元調用

unit Unit1;
interface
uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;
type
  TForm1 = class(TForm)
   Button1: TButton;
   procedure Button1Click(Sender: TObject);
  end;
var
  Form1: TForm1;
implementation
{$R *.dfm}
{implementation 區中的過程或函數, 只能在本單元調用}
function MyFun(x,y: Integer): Integer;
begin
  Result := x + y;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
  i: Integer;
begin
  i := MyFun(1,2);
end;
end.

//要點13: 需要給其他單元調用, 必須在 interface 聲明, 但必須在 uses 區後面

unit Unit1;
interface
uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;
type
  TForm1 = class(TForm)
   Button1: TButton;
   procedure Button1Click(Sender: TObject);
  end;
{需要給其他單元調用, 必須在 interface 聲明, 但必須在 uses 區後面}
function MyFun(x,y: Integer): Integer; {函數聲明}
var
  Form1: TForm1;
implementation
{$R *.dfm}
function MyFun(x,y: Integer): Integer; {函數實現}
begin
  Result := x + y;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
  i: Integer;
begin
  i := MyFun(1,2);
  ShowMessage(IntToStr(i)); {3}
end;
end.

//要點14: 如果聲明在 TForm1 類內, 那它就是 TForm1 類的一個方法了

unit Unit1;
interface
uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;
type
  TForm1 = class(TForm)
   Button1: TButton;
   procedure Button1Click(Sender: TObject);
   function MyFun(x,y: Integer): Integer; {函數聲明在 TForm1 類的體內}
   {現在這個函數 MyFun 已經是 TForm1 類的一個方法了}
  end;
var
  Form1: TForm1;
implementation
{$R *.dfm}
{函數在實現區必須有 TForm1. 作為前綴}
function TForm1.MyFun(x,y: Integer): Integer;
begin
  Result := x + y;
end;
{調用}
procedure TForm1.Button1Click(Sender: TObject);
var
  i: Integer;
begin
  i := MyFun(1,2);
  ShowMessage(IntToStr(i)); {3}
end;

end.

//要點15: 調用其他單元的函數

//包含函數的單元:
unit Unit2;
interface
function MyFun(x,y: Integer): Integer; {函數必須在接口區聲明}
implementation
function MyFun(x,y: Integer): Integer; {函數必須在函數區實現}
begin
  Result := x + y;
end;
end.

//調用函數的單元:

unit Unit1;
interface
uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;
type
  TForm1 = class(TForm)
   Button1: TButton;
   procedure Button1Click(Sender: TObject);
  end;
var
  Form1: TForm1;
implementation
{$R *.dfm}
uses Unit2; {必須 uses 定義函數的單元}
procedure TForm1.Button1Click(Sender: TObject);
var
  i: Integer;
begin
  i := MyFun(1,2);     {調用函數}
//i := Unit2.MyFun(1,2); {有時為了避免重名, 需要這樣調用}
  ShowMessage(IntToStr(i)); {3}
end;
end.

//要點16: 在實現區(implementation), 自定義的方法是有順序的

function MyFunA(x: Integer): Integer;
begin
  Result := Sqr(x); {取平方}
//MyFunB(x);   {前面的函數不能調用後面的函數}
end;
function MyFunB(x: Integer): Integer;
begin
  Result := MyFunA(x) * 2; {可以調用前面的函數 MyFunA}
end;

//要點17: 如果前面的方法要調用後面的方法, 後面的方法需要提前聲明

function MyFunB(x: Integer): Integer; forward; {使用 forward 指示字提 前聲明}
function MyFunA(x: Integer): Integer;
begin
  Result := MyFunB(x) * 3; {要調用後面的方法, 後面的方法需要提前聲明}
end;
function MyFunB(x: Integer): Integer;
begin
  Result := Abs(x);
end;

//要點18: 如果函數在接口區定義了, 就無需用 forward 提前聲明了

unit Unit1;
interface
uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;
type
  TForm1 = class(TForm)
   Button1: TButton;
   procedure Button1Click(Sender: TObject);
  end;

var

Form1: TForm1;

{現在函數定義在接口區(interface)}
function MyFunA(x: Integer): Integer;
function MyFunB(x: Integer): Integer;
implementation
{$R *.dfm}
function MyFunA(x: Integer): Integer;
begin
  Result := MyFunB(x) * 3; {因為在接口區有了聲明, 前面的函數就可以調用後的 函數了}
end;
function MyFunB(x: Integer): Integer;
begin
  Result := Abs(x);
end;

{調用測試}

procedure TForm1.Button1Click(Sender: TObject);
var
  i: Integer;
begin
  i := MyFunA(-3);
  ShowMessage(IntToStr(i)); {9}
end;
end.

//要點19 : Delphi 支持過程中的方法

procedure TForm1.Button1Click(Sender: TObject);
  procedure alert(s: string);
  begin
   ShowMessage(s);
  end;
begin
  alert('萬一'); {萬一}
end;

//要點20 : 靜態數組做參數, 不能這樣使用:

function MyFun(arr: array[0..9] of Integer): Integer;var
  i: Integer;
begin
  Result := 0;
  for i in arr do Result := Result + i;
end;

//應該先把數組定義成一個類型

Type
  IntArray = array[0..9] of Integer; {先把需要的數組定義成一個類型}

//給一個靜態數組求和的函數

function MyFun(arr: IntArray): Integer;
var
  i: Integer;
begin
  Result := 0;
  for i in arr do Result := Result + i;
end;

{測試}

procedure TForm1.Button1Click(Sender: TObject);
const
  intArr: IntArray = (1,2,3,4,5,6,7,8,9,10);
var
  x: Integer;
begin
  x := MyFun(intArr);    {調用函數}
  ShowMessage(IntToStr(x)); {55}
end;

深入方法[21] - 開放數組參數

//給一個整型開放數組求和的函數

function MyFun(const arr: array of Integer): Integer;
var
  i: Integer;
begin
  Result := 0;
  for i in arr do Result := Result + i;
end;

{測試1:}

procedure TForm1.Button1Click(Sender: TObject);
var
  num: Integer;
begin
  num := MyFun([1,2,3]);
  ShowMessage(IntToStr(num)); {6}
end;

{測試2:}

procedure TForm1.Button2Click(Sender: TObject);
var
  iArr: array of Integer;
  i,x: Integer;
begin
  SetLength(iArr, 10);
  for i := Low(iArr) to High(iArr) do
  begin
   iArr[i] := i + 1;
  end;
  x := MyFun(iArr);
  ShowMessage(IntToStr(x)); {55}
end;

深入方法[22] - 指針參數

{現在這個函數並沒有 var 前綴, 也就是說參數應該不會被修改的} 

function MyFun(p: PInteger): Integer; {PInteger 是 Integer 的指針類型 }
begin
  p^ := p^ * 2;
  Result := p^;
end;

{測試}

procedure TForm1.Button1Click(Sender: TObject);
var
  i,x: Integer;
begin
  i := 8;
  
  x := MyFun(@i);      {調用函數}
  ShowMessage(IntToStr(x)); {16}
  {現在 i 的值應該不會被修改, 但...}
  ShowMessage(IntToStr(i)); {16}
{
  沒有 var 或 out 前綴的參數, 應該是傳值的;
  有 var 或 out 的參數是傳地址的;
 
  指針就是一個地址, 盡管沒有指定傳地址, 但事實上就是給了一個地址,
  所以參數值也會改變!
}
end;

深入方法[23] - 重載

{
  下面的函數重名, 但參數不一樣, 此類情況必須加 overload 指示字;
  調用時, 會根據參數的類型和個數來決定調用哪一個;
  這就是重載.
}
function MyFun(s: string): string; overload;
begin
  Result := '參數是一個字符串: ' + s;
end;
function MyFun(i: Integer): string; overload;
begin
  Result := '參數是一個整數: ' + IntToStr(i);
end;
function MyFun(x,y: Integer): string; overload;
begin
  Result := Format('參數是兩個整數: %d 和 %d', [x,y]);
end;
{測試}procedure TForm1.Button1Click(Sender: TObject);
var
  str: string;
begin
  str := MyFun('萬一');
  ShowMessage(str);   {參數是一個字符串: 萬一}
  str := MyFun(99);
  ShowMessage(str);   {參數是一個整數: 99}
  str := MyFun(6,8);
  ShowMessage(str);   {參數是兩個整數: 6 和 8}
end;
//另外還要注意關於重載和默認參數的問題, 譬如, 下面的重載是不可行 的:function MyFun(x,y: Integer): string; overload;
begin
  Result := IntToStr(x + y);
end;
function MyFun(x: Integer; y: Integer = 1): string; overload;
begin
  Result := IntToStr(x + y);
end;
{因為當我們這樣調用時: MyFun(a,b); 系統就不知道要調用哪個了!}

深入方法[24] - 方法是一個指針{自定義過程}procedure MyProc;
begin
  ShowMessage('ok');
end;
{自定義函數}
function MyFun: string;
begin
  Result := 'ok';
end;
{讀取它們的指針}
procedure TForm1.Button1Click(Sender: TObject);
var
  p: Pointer;
begin
  p := @MyProc;
  ShowMessage(IntToStr(Integer(p))); {4570984; 這是動態的}
  p := @MyFun;
  ShowMessage(IntToStr(Integer(p))); {4571008; 這是動態的}
end;
深入方法[25] - 使用方法類型//弄明白這一點, 才好使用回調函數{ 定義方法類型}
type
  TFunType = function(x: Integer): Integer; {函數類型}
  TProcType = procedure(name: string);    {過程類型}
{定義一個符合 TFunType 類型的函數}
function MyFun(x: Integer): Integer;
begin
  Result := x * 2;
end;
{定義一個符合 TProcType 類型的過程}
procedure MyProc(name: string);
begin
  ShowMessage('我是' + name);
end;
{使用}
procedure TForm1.Button1Click(Sender: TObject);
var
  Fun : TFunType; {定義一個 TFunType 類型的變量}
  Proc: TProcType; {定義一個 TProcType 類型的變量}
begin
  Fun := MyFun;  {讓變量 Fun 指向和它具有同樣參數和返回值的自定義函數 MyFun}
  Proc := MyProc; {讓變量 Proc 指向和它具有同樣參數的自定義過程 MyProc}
  {現在這兩個方法的變量 Fun、Proc 可以使用了}
  ShowMessage(IntToStr(Fun(4))); {8}
  Proc('萬一');         {我是萬一}
end;
深入方法[26] - 回調函數

//把一個方法當作另一個方法的參數, 就是回調方法, 大家習慣稱作回調函數 type
  TFunType = function(i: Integer): Integer; {聲明一個方法類型}
function MyFun(i: Integer): Integer;    {建立類型兼容的函數}
begin
  Result := i*2;
end;
{把函數當作參數, 再定義一個函數}function MyTest(x: Integer; F: TFunType): Integer;
begin
  Result := F(x);
end;
{測試}procedure TForm1.Button1Click(Sender: TObject);
var
  Fun: TFunType; {聲明一個 TFunType 的變量}
  i: Integer;
begin
  Fun := MyFun; {讓方法變量 Fun 指向和它類型兼容的一個方法}
  {測試 Fun; Fun 是一個方法變量, 現在去執行那個方法, 它就可以當作那個方法來使 用了}
  i := Fun(4);
  ShowMessage(IntToStr(i)); //8
  {把 Fun 當作參數使用; 把函數當作參數使用, 這就是回調函數}
  i := MyTest(4,Fun);
  ShowMessage(IntToStr(i)); //8
end;
深入方法[27] - 遞歸函數:

簡單示例//所謂遞歸函數, 就是自己調用自己的函數, 先來個簡單的例子:{遞 歸調用的簡單示例}
procedure alert(i: Integer = 1);
begin
  ShowMessage(IntToStr(i)); {這是方法的功能}
  Inc(i);
  if i<10 then
   alert(i);        {這是方法自調用}
end;
{測試}procedure TForm1.Button1Click(Sender: TObject);
begin
  alert; {會連續彈出 9 個對話框, 分別提示 1..9}
end;
深入方法[28] - 遞歸函數實例: 搜索當前目錄下的所有嵌套目錄

//上面一個例子不能說明遞歸函數的本質, 直接來個實用的函數吧, 剛好要 用.unit Unit1;
interface
uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;
type
  TForm1 = class(TForm)
   Button1: TButton;
   Memo1: TMemo;
   procedure Button1Click(Sender: TObject);
  end;
var
  Form1: TForm1;
implementation
{$R *.dfm}
//列出一個目錄下所有目錄(包括嵌套)的函數
procedure GetDirs(dirName: string; List: TStrings);
var
  SRec: TSearchRec;      {定義 TSearchRec 結構變量}
  dir: string;
const
  attr: Integer = faDirectory; {文件屬性常量, 表示這是文件夾}
begin
  dirName := ExcludeTrailingBackslash(dirName) + ''; {不知道最後是不是 ; 先去 掉, 再加上}
  dir := dirName + '*.*'; {加上 ; *.* 或 * 表示所有文件, 系統會把目錄也當作一 個文件}
  if FindFirst(dir, attr, SRec) = 0 then {開始搜索,並給 SRec 賦予信息, 返回0 表示找到第一個}
  begin
   repeat
    if (SRec.Attr = attr) and       {如果是文件夾}
     (SRec.Name <> '.') and       {排除上層目錄}
     (SRec.Name <> '..') then      {排除根目錄}
    begin
     List.Add(dirName + SRec.Name);   {用List記下結果}
     GetDirs(dirName + SRec.Name, List); {這句就是遞歸調用, 如果沒有這句, 只能搜索當前目錄}
    end;
   until(FindNext(SRec)<>0);        {找下一個, 返回0表示找到}
  end;
  FindClose(SRec);              {結束搜索}
end;
{測試}procedure TForm1.Button1Click(Sender: TObject);
var
  list: TStrings;
begin
  list := TStringList.Create;
  GetDirs('C:Downloads', list);
  Memo1.Lines := list;
  list.Free;
end;
end.

深入方法[29] - 傳址參數不能賦予常量

{給這個函數可以賦常數變量}
function Fun1(x,y: Integer): Integer;
begin
  Result := x + y;
end;
{這個函數不能賦予常數變量}
function Fun2(var x,y: Integer): Integer;
begin
  Result := x + y;
end;
{測試}procedure TForm1.Button1Click(Sender: TObject);
var
  i,a,b: Integer;
const
  j = 6;
  k = 8;
begin
  i := Fun1(1,2);
  ShowMessage(IntToStr(i)); {3}
  //i := Fun2(1,2); {這樣是 Fun2 是錯誤的, 它的參數是傳地址的, 必須用變量}
  //i := Fun2(j,k); {這樣也不行}
  {應該:}
  a := 2;
  b := 4;
  i := Fun2(a,b);
  ShowMessage(IntToStr(i)); {6}
end;

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved