使用实现接口的通用类型参数对不同类型的实例进行拆箱

时间:2018-11-14 15:33:48

标签: .net generics f#

我正在为键值存储样式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实例的列表?

1 个答案:

答案 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。我知道这不是您要的,但是我必须警告不要过多使用反射和类。这永远不会结束。考虑一种更实用,更惯用的方法。