2011-10-26 14 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

行うことができます。私はこれについても興味があります。 –

答えて

3

showNameの機能実装は、Nameプロパティを持つ標準.NETクラスに適用されます。 CatでもDogもそうではありません。代わりに、両方ともF#レコードタイプです。記録fieldにアクセスするにもかかわらず

は、これらの2例は全く異なっているF#の型推論のための標準クラスpropertyへのアクセスに文字通り似ています。

一意でないフィールド名がNameの2つのレコードタイプを定義しました。レコードタイプCatNameのインスタンスcatへのアクセスはcat.Nameであり、同様にdogの場合はdog.Nameです。しかし、showName catまたはshowName dogを試してみると、これらのレコードタイプにはNameプロパティが存在しないとコンパイラが不平を言います。これは、これらのレコードにそのようなプロパティがないためです。

補遺: 私はCatDogの両方にプロパティNicknameを追加し、元のコードに若干の修正をした私のポイントを説明するために:

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

これは私が疑ったことですが、レコードはPropertiesとしてコンパイルされたフィールドを持つ標準.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 

また、私は拡張メソッドでこの同じ問題に遭遇してきました(知られている必要があります)とF#リストの静的op_Append演算子

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 
関連する問題