F#学习之路(3) 如何组织程序(下)
?????二、名稱空間(namespace)
?????名稱空間,將一組邏輯上相關的類型、模塊放在一起,主要是為了解決名稱沖突的問題,同時也便于更好的理解程序結構。F#的名稱空間概念及定義與C#基本相似。
?????1、定義名稱空間
?????在F#中定義名稱空間,使用關鍵字namespace。
? ????
?????namespace FSharpLearning
?
?????名稱空間聲明的位置應在源文件的開頭,除注釋、部分指令外,namespace聲明前不能有語句或表達式。?????
?????名稱空間的作用域為定義開始到文件結束,除非遇到下一個名稱空間的定義。在F#中一個源代碼文件中可以定義多個名稱空間。名稱空間沒有嵌套的概念,對F#來說,名稱空間只是為了給名稱空間下的元素定義一個完全限定名稱而已。
?????C#可以在一個名稱空間中嵌套定義名稱空間,而F#不支持這種定義方式。
?
//在C#中定義嵌套名稱空間的偽碼?namespace?N1?
{?
????namespace?N2?
???{?
????public?class?Test{}?
????}?
}?
?????
?????這在c#中定義了一個N1.N2的名稱空間。
?????不過F#支持使用.號分隔(與C#一致),表示一種邏輯上的層次關系,值得說明的是,對于編譯器來說名稱空間只是為了生成長名稱(long name)而已,并沒有真正的層次關系。
?
?????namespace FSharpLearning.StringExtensions
?????
?????名稱空間的作用域中只能定義類型、模塊、異常(使用exception關鍵字定義的特殊聯合類型,編譯器會生成繼承于System.Exception的類),不能定義值,所以在名稱空間中不能出現let綁定,表達式。上一篇博客中講到了在模塊中是可以定義let綁定,函數,表達式。
?????名稱空間可以使用open關鍵字打開。在F#中,open的使用與c#的using也有不同。在c#中using語句后跟的名稱空間必須是全名稱,但在F#中不僅支持這種寫法,還有另一種使用方法。
?????
?????open FSharpLearning
?????open StringExtensions(*open FSharpLearning.StringExtensions*)
?
?????在F#中雖然沒有嵌套定義名稱空間的語法,但卻能使用open打開邏輯上嵌套的名稱空間。
?????現在我來回答上一篇博客中留下來的問題。
?????由于使用了open關鍵字,使用未限定名訪問,所以出現了同名的標識,F#會以最后一個open打開的名稱空間及模塊的名稱,同名的會隱藏。后者隱藏前者。你當然可以使用完全限定名來訪問隱藏的名稱,其次你也可能使用別名的方式訪問。??
?
#lightmodule?sw=System.Windows.Forms?
module?se=FSharpLearning.StringExtensions?
open?se?
let?s="Test".Link?
sw.MessageBox.Show("good")|>ignore?
????
?????2、定義名稱空間下的子模塊
?????上一篇博客中只講到了頂級模塊的定義,未講到子模塊的定義。F#子模塊的定義仍然使用module。
?
?
#light??namespace?FSharpLearning?
module?DateTimeExtensions?=?
??open?Math?
??type?System.DateTime?with?
??????member?x.ToNumber?=?
?????????BigNum.of_bigint?<<?BigInt.FromInt64?<|?x.Ticks??
?
你注意到不同了嗎?子模塊的定義于頂級模塊不同在于,子模塊的名稱后面接著一個等號(=)。
?????
?????module submodulename =begin
??????????(*模塊中可以定義子模塊、類型、函數、值*)
?????end
?
?????begin end在#light指令下可以省略。
?
?????子模塊與頂級模塊從概念上來講,子模塊是指嵌套在模塊之下的模塊。頂級模塊在一個名稱空間下只可以定義一個,并且必須是名稱空間下第一條語句。
?????子模塊卻可以定義多個。一個名稱空間下可以定義多個子模塊。
?
?????3、名稱空間、模塊之間的區別
?????名稱空間主要用于組織邏輯上相關的類型。而模塊主要用于定義對一個類型相關的行為。
?????名稱空間就是為一組相關的類型定義一個完全限定名稱,一是從邏輯上表示相關,二是為了解決名稱沖突
?????模塊則是為了模塊化代碼。在函數式語言中,通常把對某一個類型的操作放入到一個模塊中,主要為了表示邏輯上依附于某一個類型,也部分解決名稱沖突
從語義上來講名稱空間與模塊沒有什么不同,都是為了解決名稱沖突與模塊化編程而引入的語法。歷史上來看,名稱空間是后引入的,模塊早于名稱空間出現。
?
?????三、再議F#如何編譯及確定入口點
?????上一篇博客講過F#編譯器依據傳遞給他的源代碼文件名順序依次編譯、連接,以及編譯為可執行文件時,以傳遞給他的最后一個文件名為入口點。
然后知道這一些仍然不夠,很模糊,也并不完全正確。下面我用C#概念來描述一下規則
?????F#在編譯的過程中,與源代碼文件有很大關系。根據每一個源代碼文件,F#會生成一個程序集訪問級別的靜態類(internal static),為便于描述,我把這個靜態類稱為內部靜態類。每一個模塊生成一個靜態類。為了初始化這些靜態類,F#會為每個靜態類生成一個靜態構造函數。內部靜態類會初始化整個源代碼文件中所有的靜態成員。換句話如果一個源代碼文件中有多個模塊,那么這些模塊都會被初始化。
?????F#以傳遞他的最后一個文件名作為程序入口點,會生成一個_main的方法。這是生成可執行文件默認的規則。顯然生成dll,就沒有入口點的說法,也就不會生成這個_main方法了。
?????不過F#還允許你指定入口方法。
?
#light[<EntryPoint>]?
let?test?(a:string[])?=?
???printfn?"Hello?world"?
???System.Console.ReadKey(true)?|>ignore?
?
?????使用EntryPointAttribute屬性指定一個方法為程序入口點,這種定義方法與C#很相似。不過,這個函數必須位于你傳遞給編譯器最后一個文件中,且必須位于文件的最后。
?????對于可執行文件來說,程序執行時,入口點所在的源代碼文件(編譯器為我們生成的內部靜態類)會首先加載,如果這個內部靜態類引用了其他源代碼文件,那么就會加載那個引用的源代碼文件的內部靜態類,如果引用的是類型,會加載相關類型,如果是模塊,則會執行模塊的靜態構造函數,這種方式稱為按需加載原則。對于dll,由于沒有入口點,所以程序集加載時不會強制執行靜態初始化,只會發生在調用時,當引用某個類型或模塊時,相應的類型或模塊所在的源代碼文件的內部靜態類靜態構造器會被執行。
?
?????四、總結:
?????1、名稱空間、模塊的命名應以大寫字母開頭。
?????2、對于模塊不要使用open,但使用可選類型擴展的模塊除外,可選類型擴展的模塊應使用類型名加上Extensions。
?????名稱空間下主要包含類型及模塊,類型和模塊都有很好的封裝能力,因此大多數情況下不會發生名稱沖突的問題。而使用open打開了模塊,就增大了名稱沖突,并且也會為閱讀代碼造成影響,前面說過模塊主要是為封裝某一類型的行為或某一組邏輯上相同的行為,從某種意義上說,是模擬OO中的類的職責。F#庫的List,Map,Seq這些模塊如果使用open打開,就會造成閱讀上的困難,也會造成名稱隱藏。
?????但可選類型擴展則可以使用open打開。
?????
?????type System.DateTime with
??????????member x.ToNumber =
???????????????BigNum.of_bigint << BigInt.FromInt64 <| x.Ticks?
?
?????可選類型擴展,與C#3.0的擴展方法相似,可以擴展某一類型的功能??蛇x類型擴展,必須定義在模塊中,且擴展的類型名必須使用完全限定名稱。
?????3、源代碼文件名要使用字母開頭。
???? 4、不要輕易使用open的未限定名稱方法,這很難看出完整的名稱空間。
?????5、頂級模塊不與命名空間一同使用,命名空間與子模塊一起使用。?
?????6、不要把不相關的模塊放到一個源代碼文件中,即使是同一個名稱空間下的一組相關的模塊
?????F#在生成內部靜態類時是以源代碼文件為單位的,不管調用了哪個模塊中的值、類型、函數,任何一個在這個源代碼文件中定義的模塊,都會被靜態初始化。
?
?
?????下一篇:F#學習之路(4) 基本類型
轉載于:https://www.cnblogs.com/lvxuwen/archive/2008/08/17/1269948.html
總結
以上是生活随笔為你收集整理的F#学习之路(3) 如何组织程序(下)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 《Windows via C/C++》学
- 下一篇: Array.ForEach的委托方法