日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > C# >内容正文

C#

如果你也会C#,那不妨了解下F#(6):面向对象编程之“类”

發布時間:2023/12/4 C# 47 豆豆
生活随笔 收集整理的這篇文章主要介紹了 如果你也会C#,那不妨了解下F#(6):面向对象编程之“类” 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

前言

面向對象的思想已經非常成熟,而使用C#的程序員對面向對象也是非常熟悉,所以我就不對面向對象進行介紹了,在這篇文章中將只會介紹面向對象在F#中的使用。

F#是支持面向對象的函數式編程語言,所以你用C#能做的,用F#也可以做,而且通常代碼還會更為簡潔。我們先看下面這個用C#定義的類,然后用F#來定義。

//定義一個二維點[DebuggerDisplay("({X}, {Y})")]public class Point2D{ ? ?// 用于統計已實例化的數量private static int count = 0; ? ?// 原點public readonly Point2D OriginalPoint = new Point2D(0, 0); ? ?// 完整屬性Xprivate double x; ? ?public double X{ ? ? ? ?get { return this.x; } ? ? ? ?set { this.x = value; }} ? ?// 自動屬性Ypublic double Y { get; set; } ? ?// 到原點的距離public double LengthToOriginal{ ? ? ? ?get { return this.GetDistance(this.OriginalPoint); }} ? ?// 默認構造函數public Point2D() : this(0,0) {} ? ?// 包含兩個參數的構造函數public Point2D(double x, double y) ? ?{ ? ? ? ?this.X = x; ? ? ? ?this.Y = y;count++; // 創建新點時,數量遞增1} ? ?// 將(x,y)數值轉為Point2D對象的靜態方法public static Point2D FromXY(double x, double y) ? ?{ ? ? ? ?return new Point2D(x, y);} ? ?// 計算到指定點的距離public virtual double GetDistance(Point2D point) ? ?{ ? ? ? ?var xDif = this.X - point.X; ? ? ? ?var yDif = this.Y - point.Y; ? ? ? ?var distance = Math.Sqrt(xDif * xDif + yDif * yDif); ? ? ? ?return distance;} ? ?//重寫ToString方法public override string ToString() ? ?{ ? ? ? ?return String.Format("Point2D({0}, {1})", this.X, this.Y);} }


定義類

在F#中定義類不需要class關鍵字,除非定義一個的類。

type Point2D() = class ? ? ? ? ?//定義一個空類endtype internal Point2D() = class //定義一個空的internal類endtype Point2D() = ? ? ? ? ? ? ? ?//定義一個只包含字段x的類let mutable x = 0.0

不像C#,在F#中,類的訪問修飾符默認是public的,所以internal就需要手動指定。并且,在F#,不支持protected訪問修飾符,在F#中應該使用接口,對象表達式和高階函數來實現類似功能。


字段(Field)

在上一例代碼中我們已經定義了一個字段x,但類中用let定義的字段(包括后面介紹的函數)均為private的。若需要定義其他訪問修飾符的字段,需要使用val定義。

type Point2D() = ? ?[<DefaultValue>] val mutable public x : float

其中DefaultValue特性用于將支持零值初始化的類型(具有零值的基元類型、可為null的類等)字段初始化為零值。


屬性(Property)和索引器

使用member關鍵字定義屬性,注意F#中的屬性需要有一個對象標識符(類似C#中的this)作為前綴。

type Point2D() = ? ?let mutable x = 0.0// 定義屬性X,其中set為private的。member this.X with get() ?= x ? ? ? ? ? ? ? ? ?and private set(v) = x <- v ? ?member val Y = 0.0 with get, set ? ?//自動屬性

在F#中,this也可以使用其他名稱來代替。除了this(C++和C#的習慣),有的人還使用self(Python等的習慣)、me(VB.NET的習慣)等。若不需要在屬性或方法內部使用到,一般還習慣使用__(兩個下劃線)來作為對象標識符。

而像C#set方法中的value,F#中也需要自己指定,如例子中使用v。當然get和set都可以省略使之成為只寫或只讀屬性。

遺憾的是自動屬性中不支持分別定義訪問修飾符(如C#中的{get; private set}),只能使用完整屬性來定義。


方法(Method)

方法同樣使用member定義,而靜態方法只需在member前添加static關鍵字。

type Point2D() =…… ?//因為篇幅省略屬性X,Y的代碼// 定義一個函數來獲取指定點到當前點的距離member this.GetDistance (point:Point2D) = ? ? ? ?let xDif = this.X - point.X ? ? ? ?let yDif = this.Y - point.Y ? ? ? ?let distance = sqrt (xDif**2. + yDif**2.) ? ? ? ?distance ? ?static member FromXY (x:double, y:double) = ? ? ? ?let point = Point2D()point.X <- xpoint.Y <- ypoint

重載方法

F#類中的方法重載與C#一樣,只需重新定義一個同名成員函數,且簽名不同即可。下面實現一個接受int類型的FromXY方法:

static member FromXY (x:int, y:int) =Point2D.FromXY(float x,float y) ? ?

返回目錄

構造函數與實例化

F#中有兩種構造函數,一為主構造函數(也稱隱式構造函數),上面例子中在類型定義后面的()即表示一個無參構造函數。

另一種構造函數(顯示定義)是可選的,在類里定義一個new函數即可。但new函數必須滿足以下條件:

  • 返回類型必須為該類

  • 不使用member和對象標識符。

  • 使用一個元組或unit作為參數。

我們改造上面的代碼:

type Point2D (xValue:double, yValue:double) = ? ?let mutable x = xValue ? ?member val Y = yValue with get, set ? ?new() = Point2D(0.0,0.0)

其中,調用無參構造函數時,則使用主構造函數實例化,且兩個參數均為0.0。

若要為構造函數添加訪問修飾符,寫在其之前即可。

type internal Point2D internal (xValue:double, yValue:double) = ? ?private new() = Point2D(0.0,0.0)

需要注意的是,在類型定義之后若不提供主構造函數,F#并不像C#那樣有一個無參的構造函數。

下面的代碼能通過編譯,但你無法進行實例化:

type Point2D = class end

在類中的let綁定會在調用主構造函數時運行,但如果需要在主構造函數中執行其他操作,需要使用do綁定。

type Point2D(xValue:double, yValue:double) = ? ?let mutable x = xValue ? ?static let mutable count = 0docount <- count + 1

注意let代碼和do代碼必須在member之前。而do語句中必須返回unit,可使用ignore函數丟棄返回值。

非靜態的do語句會在實例化時執行,而靜態的do語句會在第一次使用該類型時執行。而在沒有主構造函數的類中,無法使用let和do語句

如果需要在do語句中訪問其他方法,則需要類級別的對象標識符,在類型定義后使用as關鍵字指定;若想在非主構造函數中執行額外的代碼,使用then關鍵字:

type Point2D (xValue:double, yValue:double) as self = ? ?do ?//省略其他代碼self.Print "在主構造函數中。"new() as this= //主構造函數中使用self,此處用this。定義為任何名稱都可以Point2D(0.0,0.0)then this.Print "在無參構造函數中。"member this.Print str = printfn "%s" str

但這樣有一個問題:在執行do代碼訪問Print函數時,需要self已經實例化好,但因為Y自動屬性,編譯時會在do后面插入一個Y@字段(即Y的back-end字段),此時并未初始化。即違反了“先定義后引用”的原則。

所以,若在構造函數中需要訪問類中的方法,只能將Y也更改為完整屬性,并且x和y字段的let綁定必須在do之前。

實例化

在上面代碼中的new()構造函數中,實例化了一個Point2D(0.0,0.0)對象。在F#中,實例化時可以不使用new關鍵字,但有特例,在后面會介紹。

在C#中,我們可以使用對象初始化器(Object Initializers)在初始化時對屬性進行賦值,在F#中,也有類似功能:

var pt = new Point2D() {X = 3.0}; ? //C#中使用對象初始化器 let pt = Point2D(X=3.0) ? ? ? ? //F#中使用對象初始化器,注意此處調用的是無參構造函數

到此,可將上面的FromXY方法進行簡化:

static member FromXY (x:double, y:double) =Point2D(x,y)


抽象類(Abstract Class)和密封(Sealed)

要將類定義為抽象類或密封類,只需要在類定義前加上[<AbstractClass>][<Sealed>]特性。

抽象方法和虛方法

在F#中,沒有提供virtual關鍵字來定義虛方法。而使用定義一個抽象方法,并提供默認實現來代替。

type Point2D(xValue:double, yValue:double) as this=…… ? ?// 實現開頭C#代碼中的GetDistance虛方法,此處省略其他代碼abstract GetDistance : point:Point2D -> doubledefault this.GetDistance(point) = ? ?
? ?let xDif = this.X - point.X ? ?
? ?let yDif = this.Y - point.Y ? ? ?
? ?let distance = sqrt (xDif**2. + yDif**2.)distance

關鍵字abstract用來定義抽象方法,只給出函數簽名,并且函數名前不需使用對象標識符;而default默認實現語句中也不需使用member關鍵字。

與C#一樣,在派生類中進行override關鍵字重寫基類中的虛方法:

type Point2D(xValue:double, yValue:double) =…… ? ?//重寫Object類中的ToString虛方法,此處省略其他代碼override this.ToString() = sprintf "Point2D(%f, %f)" this.X this.Y

在C#中,若要重寫非虛方法,可使用new關鍵字。F#中沒有此關鍵字,但仍然可以重寫非虛方法,只是編譯器會給出警告。類的繼承與接口的實現在下一篇中介紹。

索引器(Indexer)及切片(Slice)

索引器

索引器,顧名思義就是可以使用索引來操作對象。在F#中,只需定義一個名為Item的屬性或方法即可讓該類的對象使用索引器。當然,若Item定義為方法,則索引器為只讀的。

我們用下來的代碼定義一個可變的字符串類。

open System.Collections.Generic type WordBuilder(startingLetters : string) = ? ?let m_letters = new List<char>(startingLetters)member this.Itemwith get idx ? = m_letters.[idx]and ?set idx c = m_letters.[idx] <- cmember this.Word = new string (m_letters.ToArray())let wb = WordBuilder("I Love C#") wb.[7] <- 'F'printfn "%s" wb.Word ? ?//輸出為:"I Love F#"

注意在使用索引訪問時,需要使用訪問(.[])。

切片

切片和索引器類似,不過索引器訪問的是一個元素,而切片訪問的是數據的集合。實現切片訪問需要添加GetSlice方法,切片是只讀的。

open System.Collections.Generic type WordBuilder(startingLetters : string) =…… //其他代碼省略member this.GetSlice(lower:int option,upper:int option) =letters |> Seq.skip (lower.Value)|> Seq.take (upper.Value - lower.Value + 1)|> Seq.toArray ? ? ?let wb = WordBuilder("I Love C#") printfn "%A" wb.[2..5] ? ? ?//輸出為:[|'L'; 'o'; 'v'; 'e'|]

此切片方法并未實現完整,但已可了解實現方法。注意GetSlice的參數必須是'a option泛型類型, 其中option類型將在后面再介紹,可理解為類似于Nullable<int>。

返回目錄

結語

通過以上的介紹,熟悉C#面向對象的朋友內容應該能了解F#與C#間語法的差別了。至少也可以將C#代碼按面向對象的風格翻譯成F#代碼了,下面是文章開頭C#代碼的F#版本:

open System.Diagnostics[<DebuggerDisplay("({X}, {Y})")>]type Point2D (xValue:double, yValue:double) as self = ? ?let mutable x = xValue ?

?static let mutable count = 0 ? ?doself.OriginalPoint2 <- new Point2D()count <- count + 1member __.OriginalPoint with get () = Point2D() ? ?member this.LengthToOriginal with get () = this.GetDistance(this.OriginalPoint) ? ? ? ?new() = Point2D(0.0,0.0) ? ?member __.X with ? ? ? ?get() ?= x ? ? ? ? ? ? ? ?and private set(v) = x <- v ? ?member val Y = yValue with get, set ? ?// 一個虛函數:獲取指定點到當前點的距離abstract GetDistance : point:Point2D -> double ? ?default this.GetDistance(point) = ? ? ? ?let xDif = this.X - point.X ? ? ? ?let yDif = this.Y - point.Y ? ? ? ?let distance = sqrt (xDif**2. + yDif**2.)distance ? ? ? ?static member FromXY (x:double, y:double) =Point2D(x,y) ? ? ? ?override this.ToString() = sprintf "Point2D(%f, %f)" this.X this.Y

需要注意的是C#代碼中的OriginalPoint公開字段在此處以只讀屬性實現。主要是因為:

  • F#中的let綁定的字段只能為private,無法設置為public。

  • val綁定的顯示字段(包括自動屬性)需要在主構造函數中初始化,而OriginalPoint的類型也為Point2D,在此會形成遞歸調用而引發StackOverflowException異常。

最后再簡單說下類中let和val的區別:

  • let始終是private的,且需要有主構造函數才能定義,因為let語句在主構造函數中運行(同do語句)。

  • val默認為public的,并且可添加修飾符使之成為internal或private的。若有主構造函數,需要為val字段添加DefaultValue特性。val與member關鍵字結合使用,可聲明自動屬性。

  • 在類中訪問val字段,需要使用對象標識符,而let字段不需要。

注意,此DefaultValue位于Microsoft.FSharp.Core命名空間,不要和C#中常用的位于System.ComponentModel的DefaultValue混淆。

相關文章:

  • 如果你也會C#,那不妨了解下F#(1):F# 數據類型

  • 如果你也會C#,那不妨了解下F#(2):數值運算和流程控制語法

  • 如果你也會C#,那不妨了解下F#(3):F#集合類型和其他核心類型

  • 如果你也會C#,那不妨了解下F#(4):了解函數及常用函數

  • 如果你也會C#,那不妨了解下F#(5):模塊、與C#互相調用

  • 【送書活動】機器學習項目開發實戰

  • 《機器學習項目開發實戰》送書活動結果公布

  • F#年度調查結果概述

原文地址:http://www.cnblogs.com/hjklin/p/fs-for-cs-dev-6.html


.NET社區新聞,深度好文,微信中搜索dotNET跨平臺或掃描二維碼關注

總結

以上是生活随笔為你收集整理的如果你也会C#,那不妨了解下F#(6):面向对象编程之“类”的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。