2014-12-08 10 views
6

Posso creare un'estensione array che si applica, ad esempio, solo stringhe?È possibile creare un'estensione di array in Swift che è limitato a una classe?

+1

Non sai cosa intendi, puoi dire di più sul tuo intento? Forse un esempio? – dpassage

+0

@dpassage È possibile estendere le cose con il requisito che le classi estese aderiscano a un protocollo (https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Extensions.html), sono curioso se è possibile restringerlo ulteriormente solo a una classe, in modo che la mia estensione in Swift si applichi solo agli array di stringhe. –

+0

Ma descrivi ciò che vuoi come estensione a _do_. È solo una questione di una funzione? Quindi definire una funzione generica globale. Dare un caso d'uso effettivo per ciò che si spera di ottenere. – matt

risposta

12

Al Swift 2, questo può ora essere realizzato con protocollo estensioni, che forniscono metodo e proprietà implementazioni tipi conformi (opzionalmente limitati da vincoli aggiuntivi).

Un semplice esempio: Definire un metodo per tutti i tipi conformi a SequenceType (come Array) quando l'elemento sequenza è una String:

extension SequenceType where Generator.Element == String { 
    func joined() -> String { 
     return "".join(self) 
    } 
} 

let a = ["foo", "bar"].joined() 
print(a) // foobar 

Il metodo di estensione non può essere definito per struct Array direttamente, ma solo per tutti i tipi conforme ad alcuni protocolli (con vincoli opzionali). Quindi uno deve trovare un protocollo a cui è conforme Array e che fornisce tutti i metodi necessari. Nell'esempio sopra, questo è SequenceType.

altro esempio (una variazione di 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 

Qui il metodo è definito come un'estensione CollectionType perché necessita accesso pedice agli elementi, e gli elementi vengono richiesto di essere Comparable.

+0

Puoi cambiare 'Indice == Int' a' Indice: RandomAccessIndexType' con alcune modifiche minori, ad es. 'startIndex' invece di' 0', 'endIndex.predecessor()', 'let mid = lo.advancedBy (lo.distanceTo (hi)/2)' più un bail anticipato su 'isEmpty'. Significherebbe, ad esempio, che funzionerebbe con 'String.UTF16View', che è ad accesso casuale ma non intero indicizzato. –

+0

@AirspeedVelocity: Fatto :) –

5

AGGIORNAMENTO: Vedere la risposta di Martin sotto per gli aggiornamenti di Swift 2.0. (Non posso cancellare questa risposta in quanto è accettata, se Doug può accettare la risposta di Martin, io elimino questo per evitare confusioni.)


Questo ha messo più volte nei forum, e la risposta è no, non puoi farlo oggi, ma capiscono che è un problema e sperano di migliorarlo in futuro. Ci sono cose che vorrebbero aggiungere a stdlib che ha anche bisogno di questo. Ecco perché ci sono così tante funzioni gratuite è stdlib. La maggior parte di questi sono work-around per questo problema o il problema di "nessuna implementazione predefinita" (ad esempio "tratti" o "mixin").

+2

Grazie, sembra una strana omissione. Terrò le dita incrociate. –

+0

Potrebbe voler modificare questo per 2.0, esp. come se vi si riferissero i dupes. –

0

Non hai ancora fornito un caso d'uso, nonostante molte richieste nei commenti, quindi è difficile sapere cosa stai cercando. Ma, come ho già detto in un commento (e Rob ha detto in una risposta), non lo capirai letteralmente; le estensioni non funzionano in questo modo (al momento).

Come ho detto in un commento, quello che vorrei fare è racchiudere l'array in una struttura. Ora la struct custodisce e garantisce il tipo di stringa, e abbiamo l'incapsulamento. Ecco un esempio, anche se naturalmente è necessario tenere a mente che hai dato alcuna indicazione del tipo di cosa vuoi veramente fare, quindi questo potrebbe non essere direttamente soddisfacente:

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} 
    } 
} 

Ed ecco come chiamarla:

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

Così abbiamo isolato efficacemente la nostra matrice di stringhe in un mondo in cui possiamo armare con funzioni che si applicano solo a matrici di stringhe. Se pepboys non fosse un array di stringhe, non avremmo potuto avviarlo in un StringArrayWrapper per cominciare.

+0

Se la matrice è probabile che sia grande, una classe potrebbe essere migliore di una struttura, in modo da garantire il pass-by-reference. Spesso avvolgo i grandi array nelle classi proprio per questo motivo, in modo che il loro passaggio non comporti alcuna copia. – matt

+0

Swift utilizza il copy-on-write per il pass-by-value, quindi nella maggior parte dei casi non dovrebbe esserci un costo maggiore nel passare grandi array. Se trovi un costo maggiore, dovresti aprire un radar. Apple ha notato in particolare che non si dovrebbe cercare di evitare la copia per motivi di prestazioni. Vedere la nota in fondo a https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/ClassesAndStructures.html –

+0

@RobNapier La proprietà qui è un 'var' e l'esempio presuppone che sarà essere scritto a – matt

1

Questo è già stato risposto dai tre saggi sopra ;-), ma umilmente offro una generalizzazione della risposta di @ Martin. Possiamo scegliere come target una classe arbitraria usando il protocollo "marker" che viene implementato solo sulla classe che vogliamo targetizzare. Vale a dire. non è necessario trovare un protocollo per-se, ma può creare un banale per l'utilizzo nel targeting della classe desiderata.

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()) 
Problemi correlati