程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
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