Is it possible to make an Array extension in Swift that is restricted to one class?

后端 未结 4 743
暖寄归人
暖寄归人 2020-11-27 21:39

Can I make an Array extension that applies to, for instance, just Strings?

相关标签:
4条回答
  • 2020-11-27 21:39

    As of Swift 2, this can now be achieved with protocol extensions, which provide method and property implementations to conforming types (optionally restricted by additional constraints).

    A simple example: Define a method for all types conforming to SequenceType (such as Array) where the sequence element is a String:

    extension SequenceType where Generator.Element == String {
        func joined() -> String {
            return "".join(self)
        }
    }
    
    let a = ["foo", "bar"].joined()
    print(a) // foobar
    

    The extension method cannot be defined for struct Array directly, but only for all types conforming to some protocol (with optional constraints). So one has to find a protocol to which Array conforms and which provides all the necessary methods. In the above example, that is SequenceType.

    Another example (a variation of How do I insert an element at the correct position into a sorted array in Swift?):

    extension CollectionType where Generator.Element : Comparable, Index : RandomAccessIndexType {
        typealias T = Generator.Element
        func insertionIndexOf(elem: T) -> Index {
            var lo = self.startIndex
            var hi = self.endIndex
            while lo != hi {
                // mid = lo + (hi - 1 - lo)/2
                let mid = lo.advancedBy(lo.distanceTo(hi.predecessor())/2)
                if self[mid] < elem {
                    lo = mid + 1
                } else if elem < self[mid] {
                    hi = mid
                } else {
                    return mid // found at position `mid`
                }
            }
            return lo // not found, would be inserted at position `lo`
        }
    }
    
    let ar = [1, 3, 5, 7]
    let pos = ar.insertionIndexOf(6)
    print(pos) // 3
    

    Here the method is defined as an extension to CollectionType because subscript access to the elements is needed, and the elements are required to be Comparable.

    0 讨论(0)
  • 2020-11-27 21:40

    This has already been answered by the three wise-men above ;-) , but I humbly offer a generalization of @Martin's answer. We can target an arbitrary class by using "marker" protocol that is only implemented on the class that we wish to target. Ie. one does not have to find a protocol per-se, but can create a trivial one for using in targeting the desired class.

    protocol TargetType {}
    extension Array:TargetType {}
    
    struct Foo  {
        var name:String
    }
    
    extension CollectionType where Self:TargetType, Generator.Element == Foo {
        func byName() -> [Foo] { return sort { l, r in l.name < r.name } }
    }
    
    let foos:[Foo] = ["c", "b", "a"].map { s in Foo(name: s) }
    print(foos.byName())
    
    0 讨论(0)
  • 2020-11-27 21:51

    You still haven't given a use case, despite many requests in comments, so it's hard to know what you're after. But, as I've already said in a comment (and Rob has said in an answer), you won't get it literally; extensions don't work that way (at the moment).

    As I said in a comment, what I would do is wrap the array in a struct. Now the struct guards and guarantees the string's type, and we have encapsulation. Here's an example, though of course you must keep in mind that you've given no indication of the kind of thing you'd really like to do, so this might not be directly satisfying:

    struct StringArrayWrapper : Printable {
        private var arr : [String]
        var description : String { return self.arr.description }
        init(_ arr:[String]) {
            self.arr = arr
        }
        mutating func upcase() {
            self.arr = self.arr.map {$0.uppercaseString}
        }
    }
    

    And here's how to call it:

        let pepboys = ["Manny", "Moe", "Jack"]
        var saw = StringArrayWrapper(pepboys)
        saw.upcase()
        println(saw)
    

    Thus we have effectively insulated our string array into a world where we can arm it with functions that apply only to string arrays. If pepboys were not a string array, we couldn't have wrapped it in a StringArrayWrapper to begin with.

    0 讨论(0)
  • 2020-11-27 21:57

    UPDATE: Please See Martin's answer below for Swift 2.0 updates. (I can't delete this answer since it is accepted; if Doug can accept Martin's answer, I'll delete this one to avoid future confusion.)


    This has come up several times in the forums, and the answer is no, you can't do this today, but they get that it's a problem and they hope to improve this in the future. There are things they would like to add to stdlib that also need this. That's why there are so many free functions is stdlib. Most of them are work-arounds for either this problem or the "no default implementation" problem (i.e. "traits" or "mixins").

    0 讨论(0)
提交回复
热议问题