您可能知道下面的适配器示例:
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#编译器限制?
答案 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类。虽然Cat
和Dog
都不是这样的;相反,两者都是 F#record 类型。
尽管访问记录field
看起来字面上相似,但访问F#类型推断的标准类property
,这两种情况完全不同。
您已定义了两个具有非唯一字段名称Name
的记录类型;对记录类型Name
的实例cat
的字段Cat
的访问权限为cat.Name
,对于dog
,dog.Name
的访问权限类似showName cat
。但是当您尝试showName dog
或Name
编译器抱怨这些记录类型中的Nickname
属性时,这是预期的行为,因为这些记录中没有这样的属性。
<强>附录:强>
为了说明我的观点,我对原始代码稍加修改,将属性Cat
添加到Dog
和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
最后,编译器将禁止同时使用{{1}}消息同名的记录类型的字段和属性。