2011-10-26 72 views
3

你可能知道下面的適配器例如:靜態解析類型的記錄

type Cat() = 
member this.Walk() = printf "Cat walking" 

type Dog() = 
    member this.Walk() = printf "Dog walking" 


let inline walk (animal : ^T) = 
    (^T : (member Walk : unit -> unit) (animal)) 

let cat = new Cat() 
let dog = new Dog() 

walk cat 
walk dog 

不同版本的步行路程,是您的貓與狗類的靜態編譯。

然後我嘗試了以下內容:

type Cat = { Name : string } 
type Dog = { Name : string } 

let inline showName (animal : ^T) = 
    let name = (^T : (member Name : string) (animal)) 
    printf "%s" name 

let cat = { Name = "Miaou" } : Cat 
let dog = { Name = "Waf" } : Dog 

showName cat 
showName dog 

,但我得到以下編譯錯誤:

The type 'Dog' does not support any operators named 'get_Name' 

與同爲類目錄。

但是當探索兩個記錄的生成類時,它實際上包含此生成的Name屬性的get_Name方法。

是否有不同的語法來訪問靜態解析的泛型中的記錄字段,還是F#編譯器的限制?

+0

我碰到與擴展方法同樣的問題(應該知道)和F#列出了靜態op_Append操作。我也很好奇。 – 2011-10-26 12:25:54

回答

3

您的showName函數實現適用於具有Name屬性的標準.NET類。儘管CatDog都不是這樣;相反兩者都是F#記錄類型。

儘管訪問記錄field字面上看起來類似來訪問標準的類property F#的類型推斷這兩種情況是完全不同的。

您已經定義了兩個記錄類型,其中包含非唯一字段名稱Name;訪問記錄類型Cat的實例cat的字段Namecat.Name,類似地對於dog它是dog.Name。但是當你嘗試showName catshowName dog編譯器會抱怨在這些記錄類型中缺少Name屬性,這是預期的行爲,因爲這些記錄中沒有這樣的屬性。

附錄: 爲了說明我的觀點,我做了輕微修改原來的代碼添加屬性NicknameCatDog

type Cat = { Name : string } member x.Nickname = x.Name 
type Dog = { Name : string } member x.Nickname = x.Name 

let inline showName (animal : ^T) = 
    let name = (^T : (member Nickname : string) (animal)) 
    printfn "%s" name 

let cat = { Name = "Miaou" } : Cat 
let dog = { Name = "Waf" } : Dog 

showName cat 
showName dog 

這將愉快地工作。

以修改類簽名的通知:現在是

type Cat = 
    {Name: string;} 
    with 
    member Nickname : string 
    end 

最後,編譯器將禁止兼具的字段和一個The member 'Xyzzy' can not be defined because the name 'Xyzzy' clashes with the field 'Xyzzy' in this type or module消息類似命名的記錄類型的屬性。

+0

+1。代碼對常規屬性運行不變 –

+0

這就是我所懷疑的,但是因爲記錄被編譯爲標準的.Net類,並將字段編譯爲屬性,所以我期待它的工作。它有點悲傷,成員訪問語法只適用於.Net方法和屬性(而不是.Net字段,F#記錄字段等)... – thinkbeforecoding

+0

@RobertJeppesen:當然,它確實**運行**不變;請檢查'typeof .GetProperty(「Name」)。GetValue(cat,null)'將返回'Miaou',就像您期望從常規屬性中獲得的一樣。但是,它如何幫助引用**編譯時不存在的屬性**? –

4

值得一提的是記錄類型可以實現接口。

type INamedObject = 
    abstract Name : string 

type Cat = 
    { Name : string } 
    interface INamedObject with 
    member this.Name = this.Name 

type Dog = 
    { Name : string } 
    interface INamedObject with 
    member this.Name = this.Name 

let showName (namedObject : INamedObject) = 
    printf "%s" namedObject.Name 

你也可以做

type Cat = { Name : string } 
type Dog = { Name : string } 

let showName (animal : obj) = 
    let name = 
    match animal with 
    | :? Cat as cat -> cat.Name 
    | :? Dog as dog -> dog.Name 
    | _ -> invalidArg "animal" "Not an animal" 
    printf "%s" name