微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

泛型 – 你可以在Swift中指定一个返回类型作为特定类型的任何集合吗?

我已经实现了一个自定义队列对象,我想用它来存储控制器中的先前项目.我想在我的控制器中将此类型用作私有变量,并且仅将其作为简单的CollectionType兼容对象公开给外部,以便客户端可以迭代或索引对象,而无需了解任何特定于类的详细信息,例如clear()函数.

Swift协议不能是通用的,所以不幸的是我不能简单地定义一个getter来返回CollectionOf< Type>.我已经使用以下抽象基类实现了这种行为,并从中继承了我的集合,但我希望可能有更多的Swift-y和内置方法来实现这一点,希望也不需要子类化:

class AnyCollectionOf<MemberT,IndexT: ForwardindexType>: CollectionType {
    // Sequence Type
    func generate() -> GeneratorOf<MemberT> {
        fatalError("must override")
    }

    // Collection Type
    typealias Index = IndexT
    typealias Element = MemberT

    subscript (index: Index) -> Element {
        get {
            fatalError("must override")
        }
    }

    var startIndex: Index {
        get {
            fatalError("must override")
        }
    }

    var endindex: Index {
        get {
            fatalError("must override")
        }
    }
}

解决方法

不幸的是,你的标题问题的答案是否定的:没有办法将CollectionType作为可以用作变量或返回类型的独立类型进行陪审.

SequenceType和CollectionType等协议要求实现它们的类提供类型化,以填充实现细节,例如元素类型,如上所述.一旦协议添加了这些要求,它就再也不能用作独立类型.您只能根据符合它的特定类声明接口. (如果你试图解决这个问题,你可能还记得看到关于“关联类型要求”的编译器错误不是很有帮助.)

这是你不能写的基本原因

func countItems(collection: CollectionType) -> Int { ... }

但必须改为写

func countItems<T: CollectionType>(collection: T) -> Int { ... }

后一种形式确保编译器可以访问实现CollectionType协议的实际类型的对象(T).

但是,如果您考虑封装而不是继承,那么您可能仍然可以更清晰地实现您要执行的操作.除了核心的CollectionType方法之外,您可以使用一个简单的包装器来阻止访问所有内容

struct ShieldedCollection<UCT: CollectionType> : CollectionType
{
    private var underlying: UCT

    func generate() -> UCT.Generator { return underlying.generate() }
    subscript(index: UCT.Index) -> UCT.Generator.Element { return underlying[index] }
    var startIndex: UCT.Index { return underlying.startIndex }
    var endindex: UCT.Index { return underlying.endindex }
}

var foo = [1,2,3]
var shieldedFoo = ShieldedCollection(underlying: foo)

(这里,UCT =“底层集合类型”.)

ShieldedCollection仍然具有CollectionType的所有常见类型,但由于这些可以从上下文推断出来,因此您不必明确指定它们.

这种通用方法的缺陷,不幸的是它是一个相当重要的方法,是底层类型仍然泄漏到API中.上例中的shieldedFoo类型是

ShieldedCollection<Array<Int>>

由于您的基础集合是自定义对象,因此其名称仍可能在API中泄漏,即使客户端无法直接访问该类本身.请注意,这不是一个功能问题,因为不应该通过ShieldedCollection包装器访问底层对象.此外,消费者永远不必自己编写类型 – 他们可以只使用prevIoUsItems()的结果作为CollectionType,编译器将解开所有内容.

如果您真的想要隐藏所有提及的底层集合类型,您可以通过在ShieldedCollection中移动UCT定义来编写上面包装器的特定于任务的模拟:

struct ShieldedCollection<T> : CollectionType    // Changed!
{
    typealias UCT = [T]                          // Added!

    private var underlying: UCT                  // Everything else identical

    func generate() -> UCT.Generator { return underlying.generate() }
    subscript(index: UCT.Index) -> UCT.Generator.Element { return underlying[index] }
    var startIndex: UCT.Index { return underlying.startIndex }
    var endindex: UCT.Index { return underlying.endindex }
}

var foo = [1,3]
var shieldedFoo = ShieldedCollection(underlying: foo)

在这里,您通过放弃完全的通用性来使返回类型整洁 – 此版本的ShieldedCollection仅适用于作为数组的基础CollectionTypes. (当然,您只需在UCT的类型中替换您自己的自定义集合类型.)

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。

相关推荐