2015-01-03 6 views
10

Próbuję zaimplementować rozszerzenie Dictionary i chcę obsługiwać opcjonalne wartości. Ale cokolwiek robię, jeśli używam mojej metody w słowniku [String: String?], nie można jej powiązać opcjonalnie. Jak napisać rozszerzenie do słownika, który z wdziękiem obsługuje opcjonalne wartości?Jak napisać rozszerzenie języka, które obsługuje wartości opcjonalne


Rozważmy następujące rozszerzenia:

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

więc rozważyć następujący kod:

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

I to ciekawie donosi

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

mogę napisać nierozszerzaniu metoda, która obsługuje dict Parametry jonowe z opcjonalnymi wartościami, ale nie widzą, jak to zrobić jako rozszerzenie Dictionary.

+0

mogę wyjaśnić, dlaczego to nie działa jak wydaje się spodziewać. Po zastosowaniu do Opcjonalnego, 'as',' is', 'as?' I '==' (i inne komparatory) mają specjalne znaczenie: są one stosowane do owiniętych rzeczy. Powód, dla którego 'if let valueString = value as? String' nie robi tego, czego się spodziewasz, jest to, że podczas kompilacji nie wiemy, że 'value' _is_ jest opcjonalne, więc nie używamy specjalnego znaczenia' as' zastosowanego do Opcjonalnego. Staramy się rzucić opcjonalny _itself_ na ciąg i oczywiście to się nie udaje; nie możesz rzucić opcjonalnie _itself_ na nic. – matt

+1

Jak sam powiedziałeś, jak sądzę, Swift wydaje się zapraszać do stosowania metod instancji, ale faktyczna moc jest w globalnych funkcjach najwyższego poziomu, ponieważ pozwalają ci stworzyć ogólne ograniczenie, które pozwala tylko na prawidłowy typ przez siatkę, do pewnego stopnia. Słownik ma charakter ogólny i nie można dodawać dalszych ograniczeń do elementu generycznego, więc nigdy nie będzie można tego zrobić przy użyciu metody instancji w rozszerzeniu. – matt

Odpowiedz

4

Możesz to zrobić z refleksją. Nie wymaga dużo więcej kodu niż masz już:

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

Zwraca

foo = bar 

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

Zwraca

foo = nil cannot be cast to `String` 

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

Zwraca

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

Nie otrzymuję żadnych problemów po wstawieniu ": String?" zaraz po wartościString, jak pokazano poniżej:

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

Interesujące. Kiedy to zrobię, nie zawiedzie i spadnie do "nie można rzucić" (to znaczy zwraca 'true', ale' valueString' jest zawsze 'nil', nawet jeśli podano wartość. – Rob

Powiązane problemy