这是一段代码,经过几次试验和错误后才能运行,但我不明白为什么它以这种特殊方式工作?它是按设计制定的,规则是在一些规范中写成的,或者在这种情况下是偶然的吗?代码中的注释解释了这一切,但具体问题是:
为什么类型B_from_A
没有看到IA.Item
的插槽已经由父A
实现,但是它只允许部分实现IA
,所以它实际上看到SharedMethod
已在父级中实现。
open System
open System.Collections.Generic
type IA = // only read methods
abstract Item : int -> int with get
abstract SharedMethod : int -> int
type IB = // allows in-place changes
inherit IA
abstract Item : int -> int with get, set
type IC = // immutable, returns new version with changes
inherit IA
abstract Set: int -> int -> IC
type A() =
let dic = Dictionary<int,int>() // some complex internal data structure
member internal this.Dic = dic
member this.SharedMethod(i) = pown dic.[i] 2
interface IA with
member this.Item with get(i) = dic.[i]
member this.SharedMethod(i) = this.SharedMethod(i) // custom operation on item
type B_from_A() =
inherit A()
// without this partial implementation I get an error:
// Script1.fsx(111,18): error FS0361: The override 'get_Item : int -> int'
// implements more than one abstract slot, e.g. 'abstract member IB.Item : int -> int with get'
// and 'abstract member IA.Item : int -> int with get'
interface IA with // partial interface implementation
member this.Item with get(i) = this.Dic.[i] // required to remove error FS0361
// !!! but there is no this.SharedMethod(i) here, so the type knows that this slot is
// implemented by parent A. Why it asks me to explicitly add Item get here?
interface IB with
member this.Item
with get(i) = this.Dic.[i] // implements more than one abstract slot without IA above
and set i v = this.Dic.[i] <- v
type B() = // independent implementation
let dic = Dictionary<int,int>()
interface IA with
member this.Item with get(i) = dic.[i]
member this.SharedMethod(i) = pown dic.[i] 2
interface IB with
member this.Item
with get(i) = dic.[i]
and set i v = dic.[i] <- v
// If go from B to A, type A_from_B() won't be able to hide mutation methods in IB?
// It is more natural to add functionality than to hide or block it like some SCG ReadOnly collections do (e.g. throw on Add with InvalidOp)
// Therefore keep data structure inside A but add methods to change it inside B_from_A
我还可以在哪里快速阅读F#多态实现中的抽象槽和所有低级机制?
答案 0 :(得分:2)
此行为完全由规范
定义首先来自8.14.3接口实现:
接口实现的每个成员都按如下方式进行检查:
·该成员必须是实例成员定义。
·应用调度槽推理(第14.7节)。
·在假设“this”变量具有的情况下检查成员 封闭式。
然后引用的部分:
14.7调度插槽推断F#编译器将Dispatch Slot Inference应用于对象表达式和类型定义之前 处理他们的成员。对于对象表达式和类型 定义,以下是对Dispatch Slot Inference的输入:
·正在实施的类型ty0。
·一套 成员覆盖x.M(arg1 ... argN)。
·一组额外的 接口类型ty1 ... tyn。
·另一组成员 覆盖每个tyi的x.M(arg1 ... argN)。
调度插槽推断 将每个成员与唯一的抽象成员或接口相关联 收集的类型tyi定义或继承的成员。
因此,每个函数只能运行一次 - 您无法获得所需的双重实现。
答案 1 :(得分:2)
您描述的行为发生是因为IB
继承了IA
,它们都定义了Item
属性访问器。因此,B_From_A.IB.Item
属性访问器实现有两个可用的插槽。但是,当您添加B_From_A.IA.Item
实施时,一个广告位已被占用,因此类型推断不会产生任何歧义 - IB.Item
只有B_From_A.IB.Item
可用于实施。
为了理解底层机制,重要的是要知道在F#中实现接口层次结构有两种不同的方法:
// ----- A hierarchy of interfaces
type IFoo = abstract FooMember: int
type IBar =
inherit IFoo
abstract BarMember: int
// Approach 1: Implement IFoo "explicitly".
// In the object browser, you will see both IFoo and IBar as parents of FooBar.
type FooBar =
interface IFoo with member this.FooMember = 0
interface IBar with member this.BarMember = 0
// Approach 2: Implement IFoo "implicitly" (as part of IBar).
// In the object browser, you will only see IBar as parent of FooBar.
type FooBar =
interface IBar with
member this.FooMember = 0
member this.BarMember = 0
现在,如果IFoo
和IBar
都有一个具有完全相同签名的成员(例如,在您的示例中,Item
属性访问者),并且您实现了成员只能通过FooBar.IBar.Item
,然后必然会出现类型推断歧义,因为FooBar.IBar.Item
应该实现IBar.Item
或IFoo.Item
是不明确的行为。
因此,您的问题的答案是:
B_from_A
没有看到IA.Item
的插槽已经由父A
实现了”:您的前提是不正确的。编译器会看到它,但它无法知道B_from_A.IB.Item
是否应该
IA.Item
或IB.Item
IA
的部分实现,因此它实际上看到SharedMethod
已经在父级中实现了。”如上所述,它看到IA.SharedMethod
和IA.Item
都已由父A
实现,但模糊性与“看到或未看到”任何预先存在的实现无关A
。