全國最多中醫師線上諮詢網站-台灣中醫網
發文 回覆 瀏覽次數:1523
推到 Plurk!
推到 Facebook!

新時代的開端:DELPHI.NET

 
suckin
一般會員


發表:13
回覆:26
積分:7
註冊:2003-06-16

發送簡訊給我
#1 引用回覆 回覆 發表時間:2004-04-13 09:10:24 IP:61.221.xxx.xxx 未訂閱
文/黃忠成(2004/1/1,原文刊登於PC 電腦傳真月刊) DELPHI.NET 在經過近兩年的漫長等待後,Borland 終於在2003 年的耶誕前夕釋出了DELPHI 8 for .NET Framework 的正式版本,此舉不但粉碎了DELPHI 已死的揣測,也正式將DELPHI 使用者帶入了.NET 的世界。藉由VCL.NET,原DELPHI 使用者可以延用以往所開發的 元件或是程式,用幾乎相同的方式撰寫.NET Windows Application。除此之外, DELPHI.NET 也支援.NET WinForm 及ASP.NET 的應用程式開發,這就像是DELPHI 一 貫的傳統:支援最多的環境,提供多樣化的開發技術,讓使用者得到最大的應用空間。 當然,不可否認,DELPHI.NET 畢竟是一個新的開發環境及一個加強後的DELPHI 語言, 要說完全無痛昇級是不可能的,程式設計師應該要有一個認知,在Windows 時代,API 是不可缺的知識,在今日的.NET 時代也不例外,了解.NET Framework 是基本的要求。 針對DELPHI.NET 所提供的各種新功能及新語言特色,筆者規畫了一系列文章,希望 能幫助原DELPHI 的使用者快速的進入DELPHI.NET 的世界,現在就讓我們開始這一趟 旅程吧。 Imporved! Namespace(命名空間) 事實上,Namespace 並非是在DELPHI.NET 中第一次出現,早在DELPHI 時代就已具 備基本的Namespace 概念,只是當時此技術尚未成熟,大多數時候只是用來解決模糊呼 叫或是定義等問題,下面的程式碼片段是在DELPHI 時代使用該技術的範例。 Dialogs.ShowMessage(AMsg); 當時,這種技術頂多只能稱之為Unit 的分割,離真正的Namespace 尚有段距離。為了 符合CLS 規格,DELPHI.NET 加入了真正的Namespace 機制,使編譯後的Assembly 能 為其它CLS 相容語言所用,其基本用法與以往大致相同。 unit MyCompany.MyProduct.Unit2; interface uses Borland.Vcl.Classes,Borland.Vcl.SysUtils; 與DELPHI 相同,DELPHI.NET 的Unit 檔名需與unit ...部份同名,另外原先的Classes、 SysUtils 等Unit 也都納入了Borland.Vcl 這個Namespace 之中,不過使用者並不需特別 修改以往的程式來符合這個改變,藉由編譯器預設Namespace 搜尋功能,使用者仍可使 用uses Classes,SysUtils 方式來處理,編譯器自會代為轉換。提醒讀者,目前DELPHI.NET 仍只支援在一個Unit 中定義單一Namespace,並不允許同一Unit 擁有多個Namespace。 另外DELPHI.NET Unit 也不支援類似C#等語言中以明確定義代替uses 的方式,簡單的 說就是要引用Unit2 才能使用其中的定義,不能省略掉引用Unit2 部份,直接使用 Unit2.xxx 方式宣告。 New! 成員視界 在DELPHI.NET 中,除了原有的private、protected、public、published 四個視界外,還 加入了strict private、strict protected 兩個新的成員視界,此舉是為了符合CLS 規格。原先 的DELPHI 中,private、protected 在同一Unit 中是被視同為public 視界的,下面的程式碼 說明了此點。 type TMyClass = class private FData:Integer; end; ............... vObj := TMyClass.Create; vObj.FData := 100; //OK! 這個特性仍被延用至DELPHI.NET,事實上它們被對應至Assembly or Family、Assembly and Family 兩個CLS 所規範的視界,而strict private 及strict protected 則對應到真實的 private、protected 兩個視界,表一列出目前DELPHI.NET 的視界對應及存取限制。 TMyClass = class strict private FPrivateData:Integer; private FData:Integer; end; ............. vObj := TMyClass.Create; vObj.FData := 100; //OK vObj.FPrivateData := 100; //Error DELPHI.NET C# CLS 說明 private internal Assembly 該類別或同一Assembly 才可存取。 protected internal protected Assembly or Family 同一Assembly 或子類別才可存取。 public public Public 不限。 strict private private Private 該類別中才可存取。 strict protected protected Family 該類別及其子類別才可存取。 New! class var(靜態成員變數) 對DELPHI 使用者來說,靜態成員變數是個新功能,以往多是以全域變數來達到相同 效果,使用靜態成員可以有效的將設計師由全域變數的窠臼中解放。 TMyClass = class(TObject) class var InstanceCount:Integer; .................. constructor TMyClass.Create; begin TMyClass.InstanceCount := TMyClass.InstanceCount 1; inherited Create; end; destructor TMyClass.Destroy; begin TMyClass.InstanceCount := TMyClass.InstanceCount-1; inherited Destroy; end; .................. TMyClass.Create; TMyClass.Create; TMyClass.Create; ShowMessage(IntToStr(TMyClass.InstanceCount)); // 3. class var 與一般成員相同,也具備了視界的觀念。 New! static member function(靜態成員函式) 事實上,DELPHI 中早就具備了靜態成員函式的功能,當時稱為class method, DELPHI.NET 除了繼續延用此概念外,還加入了static method 的支援,這點主要是針對 CLS 的規格而定。但是目前On-Line Help 在描述語法時似乎有誤,其內的範例是在一般 的method 後加上static 即可成為靜態成員函式,但實際上編譯器並不接受此語法,在筆 者觀察VCL 的原始碼後,正確的用法應該如下所示。 TMyClass = class(TObject) class var InstanceCount:Integer; public ............... class procedure ShowInstanceCount; static; end; class procedure 是否加上static 的關鍵字,會影響編譯器所產生的IL 程式碼,假如設計 者希望該函式能為其它CLS 相容語言所用,就必須加上static 關鍵字。 New! static properties(靜態成員屬性) 這也是一個全新的功能,DELPHI.NET 允許設計師定義靜態的成員屬性,其唯一限制 是在屬性的存取函式或是變數都必須是靜態成員。 TMyClass = class(TObject) strict private class procedure SetData(AData:Integer);static; class function GetData:Integer;static; class var FData : Integer; public class property Data:Integer read GetData write SetData; end; ......... TMyClass.Data := 100; ShowMessage(IntToStr(TMyClass.Data)); //100 New! static construtcor(靜態建構子) 在DELLPHI.NET 中,靜態建構子(class constructor)可用來初使化靜態成員。 TMyClass = class(TObject) class var FDataVar : Integer; public class constructor Create; end; .............. class constructor TMyClass.Create; begin FDataVar := 1000; end; ...... ShowMessage(IntToStr(TMyClass.FDataVar)); // 1000 基本上,DELPHI.NET 提供了完整的靜態成員支援,而其是由原本的class member 所延 伸而來。 New! Nested Type(巢狀型別) 這也是為了符合CLS 所添加的新功能,DELPHI.NET 允許設計師在某個類別中定義 一個子類別,需注意的一點是DELPHI.NET 的Nested Type 並不受視界影響,即使將子 類別宣告於strict private 之中,實際上所編譯出來的仍然是public。不過設計師仍可延用 以往將類別宣告於implementation 區段後的方式來達到與private 相同效果。 TMyClass = class(TObject) public type TMyClassInner = class(TObject) public FData:Integer; end; strict private FInner : TMyClassInner; ..................... var vObj2 : TMyClass.TMyClassInner; begin vObj2 := TMyClass.TMyClassInner.Create; .................. New! Sealed Classes(末代類別) 在CLS 規格中允許設計者定義一個seled class,此種類別將不支援繼承,DELPHI.NET 同時也支援了此功能。不過,DELPHI.NET 的on-line help 對此語法的描述是錯誤的, 下面是正確的語法。 TMyClass = class sealed public procedure HelloMyClass; end; TMyClass2 = class(TMyClass) //Error! New! Operator Overload(運算子覆載) 運算子覆載對於DELPHI 設計師來說是一個完全嶄新的概念,藉由此功能的幫助,設 計師可以自定類別的運算子行為,提供使用者一個較直覺的應用模式,例如用” ”符號 直接加總兩個物件實體,或是改變物件的型別轉換行為,下面的程式展現了此類運用。 TMyClass = class strict private FData:Integer; public class operator Add(A,B:TMyClass):TMyClass; class operator Implicit(A:Integer):TMyClass; class operator Implicit(A:TMyClass):Integer; property Data:Integer read FData write FData; end; ................... class operator TMyClass.Add(A,B:TMyClass):TMyClass; begin Result := TMyClass.Create; Result.Data := A.Data B.Data; end; class operator TMyClass.Implicit(A:Integer):TMyClass; begin Result := TMyClass.Create; Result.Data := A; end; class operator TMyClass.Implicit(A:TMyClass):Integer; begin Result := A.Data; end; procedure TForm1.FormCreate(Sender: TObject); var vObj1,vObj2,vResult,vResult2:TMyClass; vInt:Integer; begin vObj1 := TMyClass.Create; vObj2 := TMyClass.Create; vObj1.Data := 100; vObj2.Data := 100; vResult := vObj1 vObj2; ShowMessage(IntToStr(vResult.Data)); vInt := vResult; ShowMessage(IntToStr(vInt)); vResult2 := vInt; ShowMessage(IntToStr(vResult2.Data)); end; 程式中只展示了隱含轉型的覆載,DELPHI.NET 也支援明確轉型的覆載,只要將Implicit 改為Explicit 即可。DELPHI.NET 的運算子覆載技術完全相容於CLS 規格,其它支援運 算子覆載的CLS 語言,如C# 皆可直接引用。不過設計師為此特色興奮之餘,也不能忘 記一點,運算子覆載是個相當嚴肅的課題,當覆載了” ”符號之後,若不覆載其它運算 符號,將令使用者感到困惑,這也是運算子覆載容易造成錯誤的原因。 New! class helper 這不僅是一個全新的功能,同時也是一個嶄新的概念,DELPHI.NET 為了達到整合 VCL.NET 與.NET Framework 的目的,加入了class helper 功能,運用此功能,設計者可 以在不改變既存類別原始設計的方式,延伸該類別,即使該類別是末代類別也不例外, 下面是一個簡單的範例。 TMyClass = class sealed public procedure HelloMyClass; end; TMyClassHelper = class helper for TMyClass public procedure HelloMyClassHelper; end; .......... procedure TMyClass.HelloMyClass; begin ShowMessage('Hello My Class!'); end; procedure TMyClassHelper.HelloMyClassHelper; begin ShowMessage('Hello My Class Helper!'); end; ............... var vObj : TMyClass; begin vObj := TMyClass.Create; vObj.HelloMyClass; vObj.HelloMyClassHelper; end; 熟悉Design Patterns 的讀者應該已發現,class helper 有點類似Adapter 及Helper 這兩個 Pattern,是的!基本上class helper 就是這兩個Pattern 的演化,只是DELPHI.NET 採取編 譯器角度來實作,並將細節隱藏。class helper 也支援繼承與虛擬函式的操作,這代表著 class helper 開啟了除繼承外的另一扇延伸之窗,下面的範例展示了此種用法。 TMyClassHelper = class helper for TMyClass public procedure HelloMyClassHelper;virtual; end; TMyClassHelper2 = class helper(TMyClassHelper) for TMyClass public procedure HelloMyClassHelper;override; end; 需特別注意的一點,因為class helper 的實作方式較一般class 不同,因此不支援strict private、strict protected 這兩個視界,同時也不能擁有成員變數,只允許靜態成員變數。 class helper 底層運作 相信有些讀者一定對於class helper 內部的實作方式很好奇,筆者也是,所以利用了 ILDASM 及DeCompiler 工具來觀察使用class helper 類別的實作,事實上,class helper 最終的實作碼大致與下面的DELPHI 程式碼相同。 TMyClass = class public FData:Integer; end; TMyClassHelper = class public class procedure ShowData(ASelf:TMyClass); end; class procedure TMyClassHelper.ShowData(ASelf:TMyClass); begin ShowMessage(IntToStr(ASelf.FData)); end; procedure TForm1.Button1Click(Sender: TObject); var vObj : TMyClass; begin vObj := TMyClass.Create; vObj.FData := 100; TMyClassHelper.ShowData(vObj); end; 其技術重點在於將class helper 中的函式編譯為靜態函式,並添加了一個參數,用來傳入 helped object。在呼叫端部份的程式碼則會被展開為呼叫該靜態函式,這些行為都是由 編譯器代勞,下面的程式可以證明這個論點。 procedure TForm1.Button1Click(Sender: TObject); var vObj : TMyClass; vType : System.Type; Methods : array of MemberInfo; begin vObj := TMyClass.Create; vObj.HelloMyClass; vObj.HelloMyClassHelper; { invoke static function of TMyClassHelper } vType := TypeOf(TMyClassHelper); vType.InvokeMember('HelloMyClassHelper',BindingFlags.InvokeMethod,Nil,Nil,[vObj]); end; 當class helper 含有繼承時,其行為會稍微改變,改採Interface 來處理,由於這個課題還 牽扯到了RTL 部份,礙於篇幅,筆者就不再深入討論了。 PS:InvokeMember 是.NET Reflection 功能的一部份,其功能是由某個型別取出其方法並 呼叫,筆者將其放在下一篇:.NET Framework 中一併討論。 Next,.NET Framework 篇 本篇文章中,筆者以快速的方式引領讀者瀏覽DELPHI.NET 所新增的語言特色,不過 由於篇幅有限,實難詳述每個特色,例如record 中能定義成員函式等較少用到的功能就 被省略了,讀者們可以參考on-line help 來得到筆者所省略掉的其它細節,下次再見了!
系統時間:2024-05-17 21:36:00
聯絡我們 | Delphi K.Top討論版
本站聲明
1. 本論壇為無營利行為之開放平台,所有文章都是由網友自行張貼,如牽涉到法律糾紛一切與本站無關。
2. 假如網友發表之內容涉及侵權,而損及您的利益,請立即通知版主刪除。
3. 請勿批評中華民國元首及政府或批評各政黨,是藍是綠本站無權干涉,但這裡不是政治性論壇!