定义将Collection保存为属性的协议

时间:2017-02-07 20:18:48

标签: swift

我想定义一个定义变量的协议" sequence"类型为" Collection"。我想要那个,因为我有几个符合标准的类型,它们都会包含不同类型的数组。例如:

protocol BioSequence {
    associatedtype T
    var sequence: Collection { get }

    // Will also implement conformance to Collection and redirect all Collection functions to the property "sequence". This is because my types that conform to BioSequence are just wrappers for an array of Nucleotide or AminoAcid with some extra functionality
}

struct DNASequence: BioSequence {
    // Will hold "sequence" of type [Nucleotide]
}

struct AminoSequence: BioSequence {
    // Will hold "sequence" of type [AminoAcid]
}

为什么我要这个?因为我需要实现符合" Collection"只有一次在BioSequence中,所有符合类型的类型都会自动继承。另外,我可以在符合标准的类型上自由添加额外的功能。

现在,当我像上面的代码一样尝试这个时,编译器说:"协议集合只能用作通用约束"。是的,我搜索了这个错误意味着什么,但我怎么能实际修复它以使我的代码工作,就像我想要的那样。或者甚至不可能做我想做的事情?

谢谢。

1 个答案:

答案 0 :(得分:2)

您可以通过在协议中使用associatedtype轻松实现此目的,该协议可以约束为Collection,从而允许符合类型在采用协议时以具体类型满足要求。

例如:

protocol CollectionWrapper : Collection {
    associatedtype Base : Collection
    var base: Base { get }
}

extension CollectionWrapper {

    var startIndex: Base.Index {
        return base.startIndex
    }

    var endIndex: Base.Index {
        return base.endIndex
    }

    func index(after i: Base.Index) -> Base.Index {
        return base.index(after: i)
    }

    subscript(index: Base.Index) -> Base.Iterator.Element {
        return base[index]
    }

    // Note that Collection has default implementations for the rest of the
    // requirements. You may want to explicitly implement them and forward them to
    // the base collection though, as it's possible the base collection implements
    // them in a more efficient manner (e.g being non random access and having
    // a stored count).
}

// S adopts CollectionWrapper and satisfies the 'Base' associatedtype with [String].
struct S: CollectionWrapper {
    var base: [String]
}

let s = S(base: ["foo", "bar", "baz"])
print(s[1]) // "bar"

关于你的评论:

  

如果我想这样使用它:let a: CollectionWrapper = S() [...]这会让我再次使用“协议只能用作通用约束”。

问题是您目前无法谈论具有相关类型要求的协议,因为编译器(this isn't a technical limitation though)不知道用于满足这些要求的类型。您可以使用Swift的AnyCollection类型橡皮擦来包装具有给定元素类型的任意Collection来解决此问题。

例如:

let a = AnyCollection(S(base: ["foo", "bar", "baz"]))

如果您需要使用CollectionWrapper中的其他协议要求,则必须实施自己的类型橡皮擦才能执行此操作。请参阅Rob's answer here了解如何解决此问题。