【问题标题】:Declare a Swift protocol which has a property return value CollectionType<Int>?声明一个具有属性返回值 CollectionType<Int> 的 Swift 协议?
【发布时间】:2016-01-10 07:24:20
【问题描述】:

有点像

protocol A {
    var intCollection: CollectionType<Int> { get }
}

protocol A {
    typealias T: CollectionType where T.Generator.Element == Int
    var intCollection: T
}

在 Swift 2.1 中可能吗?


Swift 4 更新

Swift 4 现在支持此功能! read more in here

【问题讨论】:

标签: swift protocols


【解决方案1】:

不是嵌套协议,但使用类型橡皮擦(“Any”结构)相当简单。

protocol A {
    var intCollection: AnyRandomAccessCollection<Int> { get }
}

这实际上对于返回值通常非常方便,因为调用者通常不太关心实际类型。你只需要在你的函数结束时抛出一个return AnyRandomAccessCollection(resultArray),它就可以正常工作了。许多 stdlib 现在返回 Any 橡皮擦。对于返回值问题,几乎都是我推荐的方式。它具有使A 具体化的良好副作用,因此使用起来更容易。

如果要保留CollectionType,则需要在创建需要它的函数时对其进行限制。例如:

protocol A {
    typealias IntCollection: CollectionType
    var intCollection: IntCollection { get }
}

extension A where IntCollection.Generator.Element == Int {
    func sum() -> Int {
        return intCollection.reduce(0, combine: +)
    }
}

这并不理想,因为这意味着您可以拥有 A 与错误的集合类型。他们只是没有sum 方法。您还会发现自己在很多地方重复“where IntCollection.Generator.Element == Int”。

根据我的经验,这种努力很少值得,您很快就会回到数组(无论如何它是占主导地位的 CollectionType)。但是当你需要它时,这是两种主要的方法。这是我们今天最好的。

【讨论】:

    【解决方案2】:

    您不能像在您的问题中那样直立地做到这一点,并且这里有几个关于使用协议作为类型定义的主题的线程,其内容本身包含 Self 或相关的类型要求(结果:这是不允许的)。参见例如link provided by Christik,或线程Error using associated types and generics

    现在,对于上面的示例,您可以执行以下解决方法,但是,可能会模仿您正在寻找的行为

    protocol A {
        typealias MyCollectionType
        typealias MyElementType
        func getMyCollection() -> MyCollectionType
        func printMyCollectionType()
        func largestValue() -> MyElementType?
    }
    
    struct B<U: Comparable, T: CollectionType where T.Generator.Element == U>: A {
        typealias MyCollectionType = T
        typealias MyElementType = U
        var myCollection : MyCollectionType
    
        init(coll: MyCollectionType) {
            myCollection = coll
        }
    
        func getMyCollection() -> MyCollectionType {
            return myCollection
        }
    
        func printMyCollectionType() {
            print(myCollection.dynamicType)
        }
    
        func largestValue() -> MyElementType? {
            guard var largestSoFar = myCollection.first else {
                return nil
            }
            for item in myCollection {
                if item > largestSoFar {
                    largestSoFar = item
                }
            }
            return largestSoFar
        }
    }
    

    因此,您可以在协议A 中为您的通用集合类型实现蓝图,并在“接口类型” B 中实现这些蓝图,其中还包含作为成员的实际集合财产。我采用了from here 上面的largestValue() 方法。

    示例用法:

    /* Examples */
    var myArr = B<Int, Array<Int>>(coll: [1, 2, 3])
    var mySet = B<Int, Set<Int>>(coll: [10, 20, 30])
    var myRange = B<Int, Range<Int>>(coll: 5...10)
    var myStrArr = B<String, Array<String>>(coll: ["a", "c", "b"])
    
    myArr.printMyCollectionType()    // Array<Int>
    mySet.printMyCollectionType()    // Set<Int>
    myRange.printMyCollectionType()  // Range<Int>
    myStrArr.printMyCollectionType() // Array<String>
    
    /* generic T type constrained to protocol 'A' */
    func printLargestValue<T: A>(coll: T) {
        print(coll.largestValue() ?? "Empty collection")
    }
    
    printLargestValue(myArr)    // 3
    printLargestValue(mySet)    // 30
    printLargestValue(myRange)  // 10
    printLargestValue(myStrArr) // c
    

    【讨论】:

    • 我看了一些问题和答案,解决方法并不花哨。
    • 我自己来自 OOP 背景,我在 Swift 中学到了(艰难的方式......)当事情开始变得混乱时,也许应该重新考虑我对原始问题的处理方法:面向协议编程 (POP) 与 OOP 有许多相似之处,但仍然有很大不同,我相信 Swift 作者在编写该语言时已经考虑到了这一点。所以:如果你认为事情开始变得一团糟,我的建议是回到原点,尝试找到解决手头问题或任务的替代方法。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-10-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多