Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/file/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Swift 定义将集合作为属性保存的协议_Swift - Fatal编程技术网

Swift 定义将集合作为属性保存的协议

Swift 定义将集合作为属性保存的协议,swift,Swift,我想定义一个协议,定义一个类型为“Collection”的变量“sequence”。我想要它,因为我有几个一致的类型,它们都将包含一个不同类型的数组。例如: protocol BioSequence { associatedtype T var sequence: Collection { get } // Will also implement conformance to Collection and redirect all Collection function

我想定义一个协议,定义一个类型为“Collection”的变量“sequence”。我想要它,因为我有几个一致的类型,它们都将包含一个不同类型的数组。例如:

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]
}
我为什么要这个?因为我只需要在BioSequence中实现一次对“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 = AnyCollection(S(base: ["foo", "bar", "baz"]))

关于你的评论:

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

问题是,您目前无法使用具有相关类型需求的协议进行讨论,因为编译器()不知道用于满足这些需求的类型。您可以通过使用Swift的type橡皮擦将任意
集合
包装为给定的元素类型来解决此问题

例如:

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 = AnyCollection(S(base: ["foo", "bar", "baz"]))

如果您需要处理
CollectionWrapper
中的其他协议要求,则必须实现自己的类型橡皮擦。请参阅以了解如何执行此操作。

有什么理由不直接使结构符合
集合
?因为我将有几个这样的结构,并且我不想总是实现它们的一致性。如果我在BioSequence协议中实现一次一致性,他们也会得到。在这种情况下,最好声明
协议BioSequence:Collection
并实现您需要的任何内容。这样,符合
BioSequence
使结构本身
Collection
s。如果它们应该是
Collection
-类似的,最好将它们变成实际的
Collection
s,而不是
Collection
@BallpointBen,我相信这就是他在示例中的注释所要做的—“还将实现与Collection的一致性,并将所有收集函数重定向到属性”sequence“”我的意思是,如果您只需遵守
生物序列
协议,就可以使结构本身成为集合并免费获得所有功能,那么无需重定向到实例变量。谢谢您的详细回答。如果我想这样使用:让a:CollectionWrapper=S()或让b:CollectionWrapper=AnyOtherConformingType(),这会让我再次遇到“协议只能用作泛型约束”。我猜这是因为编译器无法找出这种类型的运行时,毕竟我需要转到动态调度,将它们作为类编写并删除协议?好的,我是对的,编译器不知道这种类型。你能用AnyCollection协议的代码编辑你的答案吗?在协议中简单地用AnyCollection替换Collection至少不起作用。谢谢,非常感谢!不幸的是,这看起来不像我想要的。例如,我想写var x:BioSequence,并且知道这里只能使用符合BioSequence的类型。在AnyCollection中包装它将使程序员能够传入其他集合,即包装在AnyCollection中的集合。另外,这个额外的包装看起来很奇怪。我想我会放弃协议,用超类BioSequence和子类实现功能。运行时将能够通过动态分派来确定类型,一切都应该正常。对吗?@BoA456对我来说听起来不错,不过请注意,沿着类的路线走下去会丢失值语义。正如我提到的,创建自己的
AnyBioSequence
类型擦除器也是一个选项(这将提供仅允许符合
BioSequence
的类型的限制)。