2015-01-03 5 views
10

Sto cercando di implementare un'estensionee voglio gestire i valori opzionali. Ma qualunque cosa faccia, se uso il mio metodo su un dizionario [String: String?], non riesce a legare opzionalmente il valore. Come si scrive un'estensione a un dizionario che gestisce con garbo i valori opzionali?Come scrivere l'estensione del dizionario che gestisce i valori opzionali


Si consideri la seguente estensione:

extension Dictionary { 
    func someMethod() { 
     for (key, value) in self { 
      if let valueString = value as? String { 
       println(" \(key) = \(valueString)") 
      } else { 
       println(" \(key) = \(value) cannot be cast to `String`") 
      } 
     } 
    } 
} 

Quindi considerare il seguente codice:

let dictionary: [String: AnyObject?] = ["foo": "bar"] 
dictionary.someMethod() 

E riporta curiosamente

foo = Optional(bar) cannot be cast to `String` 

posso scrivere una non estensione metodo che gestisce dict parametri ionici con valori opzionali, ma non vedi come farlo come estensione di Dictionary.

+0

riesco a spiegare il motivo per cui non funziona come ti sembra di aspettare. Se applicato a un oggetto facoltativo, 'as',' is', 'as?', E '==' (e gli altri comparatori) hanno un significato speciale: sono applicati a ciò che è avvolto. Il motivo per cui 'se let valueString = value as? String' non fa quello che sembra che sia in fase di compilazione, non sappiamo che 'value' _is_ è un Optional, e quindi non usiamo il significato speciale di' as' applicato a un Optional. Proviamo a lanciare il _itself_ opzionale in una stringa e, naturalmente, ciò non riesce; non puoi lanciare un _itself_ Opzionale su qualcosa. – matt

+1

Come hai detto tu, credo, Swift sembra invitare l'uso di metodi di istanza, ma il potere effettivo è nelle funzioni globali di primo livello, perché ti permettono di costruire un vincolo generico che consente solo il tipo corretto attraverso la mesh, com'era. Il dizionario è un generico e non è possibile aggiungere ulteriori vincoli a un generico, quindi non sarà mai possibile farlo con un metodo di istanza in un'estensione. – matt

risposta

4

Si potrebbe fare questo con la riflessione. Non richiede molto di più di quanto il codice si dispone già di:

extension Dictionary 
{ 
    func someMethod() 
    { 
     for (key, value) in self 
     { 
      var valueRef = _reflect(value) 

      while valueRef.disposition == .Optional && valueRef.count > 0 && valueRef[0].0 == "Some" 
      { 
       valueRef = valueRef[0].1 
      } 

      if let valueString: String = valueRef.value as? String 
      { 
       print(" \(key) = \(valueString)") 
      } 
      else 
      { 
       print(" \(key) = \(value) cannot be cast to `String`") 
      } 
     } 
    } 
} 

let dictionary: [String : AnyObject?] = ["foo" : "bar"] 
dictionary.someMethod() 

Returns

foo = bar 

let dictionary: [String : AnyObject?] = ["foo" : nil] 
dictionary.someMethod() 

Consegne

foo = nil cannot be cast to `String` 

let dictionary: [String : AnyObject?] = ["foo" : UIViewController()] 
dictionary.someMethod() 

Returns

foo = Optional(<UIViewController: 0x7fee7e819870>) cannot be cast to `String` 
0

Non ho problemi quando inserisco ': String?' subito dopo valueString come mostrato di seguito:

extension Dictionary { 
    func someMethod() -> Bool { 
     for (key, value) in self { 
      if let valueString:String? = value as? String { 
       println(" \(key) = \(valueString)") 
      } else { 
       println(" \(key) = \(value) cannot be cast to `String`") 
       return false 
      } 
     } 
     return true 
    } 
} 

func doSomething() { 
    let dictionary: [String: AnyObject?] = ["foo": "bar"] 
    if dictionary.someMethod() { 
     println("no problems") 
    } else { 
     println("casting issues") 
    } 
} 
+1

Interessante. Quando lo faccio, non fallisce e cade nel "non può lanciare" (cioè restituisce 'true', ma' valueString' è sempre 'nil', anche se c'era un valore fornito. – Rob