带记录的静态解析类型

时间:2011-10-26 10:52:28

标签: generics f# type-parameter

您可能知道下面的适配器示例:

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

为Cat和Dog类静态编译了不同版本的walk。

然后我尝试了以下内容:

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'

和Cat。类相同。

但是当探索两个记录的生成类时,它实际上包含了生成的Name属性的get_Name方法。

在静态解析的泛型中访问记录字段是否有不同的语法,还是F#编译器限制?

2 个答案:

答案 0 :(得分: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

答案 1 :(得分:3)

您的showName函数实现适用于具有Name属性的标准.NET类。虽然CatDog都不是这样的;相反,两者都是 F#record 类型。

尽管访问记录field 看起来字面上相似,但访问F#类型推断的标准类property,这两种情况完全不同。

您已定义了两个具有非唯一字段名称Name的记录类型;对记录类型Name的实例cat的字段Cat的访问权限为cat.Name,对于dogdog.Name的访问权限类似showName cat。但是当您尝试showName dogName编译器抱怨这些记录类型中的Nickname属性时,这是预期的行为,因为这些记录中没有这样的属性。

<强>附录: 为了说明我的观点,我对原始代码稍加修改,将属性Cat添加到Dogtype 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

最后,编译器将禁止同时使用{{1}}消息同名的记录类型的字段和属性。