我正在为键值存储样式API的变体进行抽象设计,其中具有以下接口(为清楚起见已简化)。
type IValue =
abstract member Id: int64
type IKey<'value when 'value :> IValue> = interface end
type IKeyGenerator<'value, 'key when 'value :> IValue and 'key :> IKey<'value>> =
abstract member Generate: 'value -> 'key
type IKeyValueStore<'value when 'value :> IValue> =
abstract member Store: 'value -> unit
abstract member Get: int64 -> 'value option
abstract member Find<'key when 'key :> IKey<'value>> : 'key -> 'value option
基本上,每个值都可以通过多个键来查找,并且每个值的键都是由IKeyGenerator
生成的。当我在Store
上调用IKeyValueStore
时,我想找到给定值的所有键生成器,运行每个键生成器,并存储该值的每个键,以便随后可以由任何一个检索这些键。
我的问题是,尽管我可以反省地发现IKeyGenerator
类型参数与该'value
的{{1}}类型相匹配的'value
类型的所有实现,但我可以不能安全地将它们拆箱为一致的类型。我尝试将它们全部拆箱到IKeyValueStore
,但是,如果具体实现未以这种方式显式实现接口,则此操作将无效。如果他们通过引用IKeyGenerator<'value, IKey<'value>>
的特定实现来实现接口,则取消装箱失败。
然后我尝试引入一个简化的IKey
接口,该接口将IKeyGenerator<'value>
定义为仅返回Generate
而不是返回IKey<'value>
,从而解决了将所有实现拆箱的问题,但是随后我在不知道键的实际类型的情况下遇到了下游问题(例如,当存在多个可能的键值时,为了执行'key :> IKey<'value>
)。
如果它们都为相同类型的值实现Find
,是否可以通过某种方式安全地获取不同IKeyGenerator
实例的IKey
实例的列表?
答案 0 :(得分:3)
我建议使用密钥生成器的两层实现:实现-c 71750 72238
的基类,但是它本身具有更具体的类型参数,并受您那里的方式的约束:
IKeyGenerator
然后具有从type IKeyGenerator<'value when 'value :> IValue> =
abstract member Generate: 'value -> IKey<'value>
[<AbstractClass>]
type KeyGeneratorBase<'value, 'key when 'value :> IValue and 'key :> IKey<'value>> =
abstract member Generate: 'value -> 'key
interface IKeyGenerator<'value> with
override this.Generate v = this.Generate v :> _
继承的特定实现。
通过这种方式,实现可以使用其具体类型,而使用者将拥有期望的窄类型。
或者(或者,我更喜欢这种方式),具有创建KeyGeneratorBase
s的功能:
IKeyGenerator
P.S。我知道这不是您要的,但是我必须警告不要过多使用反射和类。这永远不会结束。考虑一种更实用,更惯用的方法。