//delphi中exit,abort,break,continue 的區別
exit: 退出函數體
abort: 遇到異常,安靜處理,就是不顯示不提示
break: 退出當前循環體,包括for ,while, repeat等循環體
continue: 結束循環內的本次處理,繼續從循環體的開始位置繼續執行
Exit 是跳出當前代碼塊,也就是當前函數,跳出后是要繼續向下執行的(如果有后續代碼)。
Abort 是從 EAbort 過來的,可以激發 exception,其實質就是 Abort = RaiseException(),是一個不出現對話框的異常。所以 Abort 的行為和異常是一樣的,其代碼執行順序也是follow異常的流程。
例如:
try
(1) //執行了
abort;
(2) //不執行
exception
(3) //執行了
end;
用 Abort 能夠執行 exception 里邊的代碼,但是如果用 Exit,就直接離開,不管 exception。
例如:
procedure p1;
begin
p2;
p3;
end;
procedure p2;
begin
abort; //exit;
end;
procedure p3;
begin
//showmessage()..
end;
如果用 Abort,則執行不到 P3,如果用 Exit 就能夠執行到 P3
******************************************************************************
在Delphi過程、函數中傳遞參數幾個修飾符為Const、Var、Out。另一種不加修飾符的為默認按值傳遞參數。
一、默認方式以值方式傳遞參數
procedure TForm1.ProcNormal(Value: string);
begin
OrigNum:=Value+' Me';
lblReturn.Caption:=OrigNum;//OrigNum為'Hello Me'
lblOrig.Caption:=Value;//Value為'Hello'
end;
調用:
OrigNum:='Hello';
ProcNormal(OrigNum);
二、以Const方式傳遞參數,這個參數在調用過程中不能改變,並且這種方式會被編譯器優化,一般建議盡可能地使用這種方式。
procedure TForm1.ProcConst(const Value: string);
begin
OrigNum:=Value+' Me';
lblReturn.Caption:=OrigNum;//為'Hello Me‘
lblOrig.Caption:=Value;//為'Hello Me'
end;
三、按引用方式傳遞參數
procedure TForm1.ProcRef(var value: string);
begin
OrigNum:=Value+' Me';
lblReturn.Caption:=OrigNum;//為'Hello Me‘
lblOrig.Caption:=Value;//為'Hello Me'
end;
四、按Out方式傳遞參數,這個方式傳遞參數時,參數可以不被初始化,即使有值也被忽視,它一般用於輸出,它可以實現在一個過程中返回多個值,我們通常在分布式對象模型,如COM中使用它。
procedure TForm1.ProcOut(out Value: string);
begin
OrigNum:=Value+' Me';
lblReturn.Caption:=OrigNum;//為'Me'
lblOrig.Caption:=Value;//為'Me'
end;
五、無類型參數,這是一種較為特殊的方法,參數的類型不確定,只能用Const、Var、Out修飾,不能用於值方式傳遞參數,具體使用示例如下:
procedure TForm1.ProcUntype(const Value);
begin
OrigNum:=string(Value)+' Me';
lblReturn.Caption:=OrigNum;//為'Hello Me'
lblOrig.Caption:=string(Value);//為'Hello Me'
end;
六、默認參數,即如果此參數在調用時未提供時,將使用默認值。
procedure TForm1.ProcDefault(const Value, constDefaultValue:string=' 123');
begin
OrigNum:=Value+' Me'+DefaultValue;
lblReturn.Caption:=OrigNum;//為'Hello Me 123'
lblOrig.Caption:=Value;// 為'Hello Me 123'
end;
七、開放數組參數,即參數數組的元素個數不確定。
procedure TForm1.ProcArray(const Value: array of string);
var
i:Integer;
begin
for i:=Low(Value) to High(Value) do
OrigNum:=OrigNum+Value[i];//調用后為'Hello abc dbd'
lblReturn.Caption:=OrigNum;
end;
調用:
OrigNum:='Hello';
ProcArray([' abc ',' dbd']);
八、無類型開放數組參數,即類型及元素個數皆不確定。在WIN32平台中,這個參數的類型實際為array ofTVarRec,其使用示例如下:
procedure TForm1.ProcArrayConst(const Value: array of const);
var
i:Integer;
begin
for i:=Low(Value) to High(Value) do
with Value[i] do
case VType of
vtAnsiString: OrigNum:= OrigNum+String(VAnsiString);
vtInteger: OrigNum:=OrigNum+IntToStr(VInteger);
vtBoolean: OrigNum := OrigNum + BoolToStr(VBoolean);
vtChar: OrigNum := OrigNum + VChar;
vtExtended: OrigNum := OrigNum + FloatToStr(VExtended^);
vtString: OrigNum := OrigNum + VString^;
vtPChar: OrigNum := OrigNum + VPChar;
vtObject: OrigNum := OrigNum + VObject.ClassName;
vtClass: OrigNum := OrigNum + VClass.ClassName;
vtCurrency: OrigNum := OrigNum + CurrToStr(VCurrency^);
vtVariant: OrigNum := OrigNum + string(VVariant^);
vtInt64: OrigNum := OrigNum + IntToStr(VInt64^);
end;
lblReturn.Caption:=OrigNum;//調用后為'Hello abc 3'
end;
調用:
OrigNum:='Hello';
ProcArrayConst([' abc ',3]);
以上就是常見幾種傳遞參數的方式。
//另一篇文章關於delphi參數傳遞的//
delphi參數傳遞
參數傳遞
聲明/實現一個過程使用的參數稱為形式參數(簡稱形參),調用過程時傳入的參數稱為實際參數(簡稱實參)。
{ Info是形參}
procedure ShowInfo(Info: String);
begin
ShowMessage(Info);
end;
var
S: String;
begin
S := 'lxpbuaa';
{S是實參}
ShowInfo(S);
end;
參數傳遞分兩種:按值(by val)和引用(by ref)。這兩種方式的本質區別是:
按值傳遞時,形參和實參是兩個變量,它們開始時的值是相同的,即實參的數據被拷貝一份傳遞給了形參。所以此時,形參的改變不會影響到實參。
引用傳遞時,形參和實參是同一個變量,可以將它們之一看做是另一個的別名。 所以此時,形參改變時,實參跟着改變。
默認情況下,參數是按值傳遞的,傳遞的是數據拷貝;如果加了var前綴,則成了引用傳遞。
我們看如下例子:
procedure TForm1.ByVal(I: Integer); {按值傳遞I}
begin
ShowMessage(IntToStr(Integer(@I)));
{取得形參所在地址。你會發現它和實參地址是不同的,因為此時實參和形參是不同的兩個變量}
I := I + 1;
end;
procedure TForm1.ByRef(var I: Integer); {引用傳遞I}
begin
ShowMessage(IntToStr(Integer(@I)));
{取得形參所在地址。你會發現它和實參地址是相同的,因為此時實參和形參是同一個變量}
I := I + 1;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
I: Integer;
begin
I := 1;
ShowMessage(IntToStr(Integer(@I))); {取得實參所在地址}
ByVal(I); { I =1}
Showmessage(i); {i:=1;實參沒有變}
ByRef(I); { I =2}
showmessage(i); {i:=2,實參改變了}
end;
按值傳遞的參數可以指定默認值,比如上面的ByVal可以是這樣:
procedure ByVal(I: Integer = 0);
調用它時可以省掉有默認值的參數:ByVal。帶默認值的參數必須位於參數列表的最后,如:
procedure ByVal(I: Integer = 0; B: Boolean);
是不行的,應該改為:
procedure ByVal(B: Boolean; I: Integer = 0);
因為默認值必須是一個常數表達式,所以dynamic-array、procedural、class、class-reference和interface等參數只能指定nil默認值;而record、variant、file和static-array等類型的參數則根本不能指定默認值。
如果按值傳遞一個指針類型的參數,情況會變得復雜而又很有意思。此時,實際傳遞的是什么呢?是實際數據的拷貝嗎?不,是指針的拷貝,也就是說形參和實參是兩個指針,不過這兩個指針指向了相同地址。所以這時候,形參和實參可以共享它們指向地址中的數據,但如果改變了形參的指針指向,實參的指針指向不能跟着改變。那么總結一下,就是:按值傳遞指針參數時,實參和形參可以共享指針指向地址中的數據,但是不能共享指針本身的指向。而引用傳遞時,因為實參和形參是同一個變量,因此實現完全共享。看下面的例子:
procedure TForm1.ByVal(Obj: TObject);
begin
Obj := Button1;
{改變形參指針指向,實參的指針指向不會跟着改變,因為它們是兩個變量。如果僅僅是改變Obj的屬性而不改變指向,則實參的屬性會跟着改變}
end;
procedure TForm1.ByRef(var Obj: TObject);
begin
Obj := Button1;
{改變形參指針指向,實參的指針指向跟着改變,因為它們是同一個變量}
end;
procedure TForm1.Button1Click(Sender: TObject);
var
Obj: TObject;
begin
Obj := Self;
{Self即Form1,所以此時實參Obj的類名(ClassName)是"TForm1"}
ByVal(Obj); {按值傳遞指針變量Obj}
ShowMessage(Obj.ClassName); {顯示類名"TForm1"}
ByRef(Obj); {引用傳遞指針變量Obj}
ShowMessage(Obj.ClassName); {顯示類名"TButton1"}
end;
上面講了這么多,最根本的還是一句話:按值傳遞時,形參和實參是兩個變量;引用傳遞時,形參和實參是同一個變量。抓住這句話,就等於抓住了一切。(ps:關鍵總結):
相信你還看到過如下格式的參數聲明:
function CompareStr(const S1, S2: string): Integer;
function TryStrToInt(const S: string; out Value: Integer): Boolean;
其中使用了const和out關鍵字。如果你沒有看到過這樣的聲明,也不要緊,它們是真實存在的。
const聲明的參數是按值傳遞的,而且形參不能被改變。
out聲明的參數是引用傳遞的,主要用於定義輸出參數,也就是說不需要輸入值(即實參不需要初始化),實參傳遞給形參的值被忽略。
如果用const修飾指針參數,那么只能通過形參修改指針地址里的數據而不能修改指針本身的指向。例如對於一個const對象參數,可以修改其屬性,但是不能將它指向其他對象。例如:
procedure ShowInfo(const Form: TForm);
begin
{以下一句不能通過,編譯器提示:[Error] Unit1.pas(28): Left side cannot be assigned to}
{Form := Form1;}
{但是通過其屬性或者方法修改隸屬於Form的數據}
Form.Caption := 'lxpbuaa';
ShowMessage(Form.Caption);
end;
在本小節的最后,還不得不提及一種很特殊的參數類型:無類型參數(Untyped parameters)。
聲明時沒有指定數據類型的參數稱為無類型參數。因此,從語法上講,無類型參數可以接收任何類型的數據。
無類型參數必須加const、out或var前綴;無類型參數不能指定默認值。
如以下一些Delphi定義的過程都使用了無類型參數:
procedure SetLength(var S; NewLength: Integer); {參數S}
procedure Move(const Source;var Dest;Count:Integer); {參數Source、Dest}
procedure TStream.WriteBuffer(const Buffer; Count: Longint);{參數Buffer}
所謂無類型參數可以接收任何類型的值,只是從語法角度而言的。或者說,理論上我們可以實現一個可以使用任何類型變量作為參數的過程,但是實際上沒有必要,也不可能做到。
打個比方說,我們想造一輛可以裝載任何物體的汽車。因為是“任何物體”,所以物體可能是任何形狀,於是這輛車必須沒有車篷,除了在幾個車輪上鋪一個足夠大(足夠大就已經是個大問題了)的平板外,不能再有任何東西。這時候,這個平板就可以看做是無類型的,因為它上面可以坐人、擺一張桌子,也可以趕一些動物上去站着或者躺着。盡管它可以承載很多種類的東西,但是也是有限制的,比如不能放一座山、也無法容納1萬頭豬。所以無類型參數的類型往往是有一定限制的。比如SetLength的參數S只能是字符串、動態數組等。
這種限制一般是在過程的實現中完成的,在運行時檢查參數值的實際類型。對於與開發環境關系緊密的參數,限制也可以構築在編譯器里。
使用無類型參數的原因是無法在聲明時使用一個統一的類型來描述運行時可能的類型,如SetLength的參數S可以是字符串和動態數組,而並沒有一個統一的類型來代表字符串和動態數組類型,所以干脆聲明為無類型。而將類型限制放到別的地方實現(如編譯器)。例如SetLength的限制規則是寫在編譯器中的,它只能作用於長字符串或者動態數組。你企圖完成下面的功能時:
var
I: Integer;
begin
SetLength(I, 10);
end;
編譯器編譯時將給出錯誤信息:[Error] Unit1.pas(35): Incompatible types。導致編譯中斷。
小結
本小節的內容比較重要,重點是理解參數按值傳遞和引用傳遞的本質:按值傳遞時,形參和實參是兩個變量;引用傳遞時,形參和實參是同一個變量。
聲明指令
聲明一個過程,可以使用register、pascal、cdecl、stdcall和safecall指令來指定參數傳遞順序和參數內存管理方式,從而影響過程的運作。如:
function MyFunction(X, Y: Integer): Integer; cdecl;
這五個指令具有不同含義,如表3-1所示。
表3-1 五個指令的不同含義
指令 參數存放位置 參數傳遞順序 參數內存管理 適用地點
register CPU寄存器 從左到右 被調用者 默認.published屬性存取 方法 必須使用
pascal 棧 從左到右 被調用者 向后兼容,不再使用
cdecl 棧 從右到左 調用者 調用C/C++共享庫
stdcall 棧 從右到左 被調用者 API調用,如回調函數
safecall 棧 從右到左 被調用者 API調用,如回調函數。雙
在一些源代碼(包括Delphi自帶的VCL源代碼)中,你還可能看到near、far、export以及inline、assemble等指令,它們是為了和16位Windows系統或者早期Pascal/Delphi兼容,在目前的Delphi版本中,已經不具有任何意義,所以在新的開發中不要再使用。