2015-01-03 6 views
10

Ich versuche, eine Dictionary Erweiterung zu implementieren, und ich möchte optionale Werte behandeln. Aber was auch immer ich tue, wenn ich meine Methode auf einem [String: String?] Wörterbuch verwende, kann es den Wert optional nicht binden. Wie schreiben Sie eine Erweiterung in ein Wörterbuch, das optionale Werte korrekt verarbeitet?Wie schreibe ich Dictionary-Erweiterung, die optionale Werte behandelt


Betrachten Sie die folgende Erweiterung:

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`") 
      } 
     } 
    } 
} 

den folgenden Code So betrachten:

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

Und es berichtet neugierig

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

ich eine nicht-Erweiterung schreiben kann Methode, die dict behandelt ionäre Parameter mit optionalen Werten, aber sehen nicht, wie es als eine Erweiterung von Dictionary zu tun.

+0

ich erklären kann, warum es nicht wie Sie arbeiten. Wenn auf ein Optional angewendet, haben 'as',' is', 'as?' Und '==' (und die anderen Komparatoren) eine besondere Bedeutung: Sie werden auf das Ding-Wrapped angewendet. Der Grund warum 'wenn valueString = Wert als? String 'tut nicht das, was Sie zu erwarten scheinen, dass wir bei der Kompilierung nicht wissen, dass' value' _is_ ein Optional ist, und deshalb verwenden wir nicht die spezielle Bedeutung von 'as', die auf ein Optional angewendet wird. Wir versuchen, das Optional _itself_ auf einen String zu übertragen, und das schlägt natürlich fehl; Sie können kein optionales _itself_ auf irgendetwas werfen. – matt

+1

Wie Sie selbst gesagt haben, scheint Swift die Verwendung von Instanzmethoden zu laden, aber die tatsächliche Stärke liegt in den globalen Funktionen der obersten Ebene, da sie Ihnen erlauben, eine generische Einschränkung zu konstruieren, die nur den richtigen Typ durch das Mesh zulässt. sozusagen. Das Dictionary ist ein generischer Code, und Sie können einem Generic keine weiteren Constraints hinzufügen, sodass Sie dies nie mit einer Instanzmethode in einer Erweiterung tun können. – matt

Antwort

4

Sie könnten dies mit der Reflexion tun. Nicht viel mehr Code benötigen, als Sie bereits haben:

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

Returns

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

Returns

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

Ich bekomme keine Probleme, wenn ich ': String?' gleich nach dem ValueString wie unten gezeigt:

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

Interessant. Wenn ich das mache, fällt es nicht aus und fällt auf "can not cast" (d. H. Es gibt "true" zurück, aber "valueString" ist immer "nil", selbst wenn ein Wert angegeben wurde. – Rob