2017-02-21 4 views
-3

Ich weiß, dass mein Wörterbuch wird entweder <String,String> oder es wird <String,NSNumber>, Gibt es eine Möglichkeit, ich kann dies Swift Compiler zur Deklaration Zeit sagen, anstatt <String,Any> oder zu deklarieren?Alternative des Wörterbuchs <String,Any>?

+0

Ich denke, warum 'Any' und' AnyObject' hilfreich sind für. –

+0

Deklarieren Sie einen Funktionsparameter 'func f (a: [String: String] | [String: NSNumber])', Rückgabewert 'func f() -> [String: String] | [String: NSNumber] 'oder etwas anderes? – kennytm

+0

'[String: Any]' ist der Weg zu gehen, vielleicht Generika zu erklären kann helfen, aber ich denke, es ist nützlicher für die Funktion param, da sie Protokoll auch annehmen können – Tj3n

Antwort

2

Sie könnten ein leeres Protokoll erstellen und machen String und NSNumber ihm entsprechen.

protocol StringOrNumber {} 

extension String:StringOrNumber {} 
extension NSNumber:StringOrNumber {} 

Es könnte helfen, explizit hinzufügen Int, Float und Double

extension Int:StringOrNumber {} 
extension Float:StringOrNumber {} 
extension Double:StringOrNumber {} 

Dann können Sie ein Wörterbuch mit Strings und Zahlen erstellen

var dict:[String:StringOrNumber] = ["a":1, "b": 3.2,"c": "c"] 

dict["d"] = "a string" 
dict["e"] = 1.3 

Aber man kann nicht Dinge hinzufügen, dass sind keine Zeichenketten oder Zahlen

dict["f"] = Date() //error: Cannot assign value of type 'Date' to type 'StringOrNumber?' 
2
enum Either<L, R> { 
    case left(L) 
    case right(R) 

    init(_ left: L) { 
     self = .left(left) 
    } 

    init(_ right: R) { 
     self = .right(right) 
    } 

    init?(_ any: Any) { 
     if let left = any as? L { 
      self = .left(left) 
     } else if let right = any as? R { 
      self = .right(right) 
     } else { 
      return nil 
     } 
    } 
} 

Und dann deklarieren Sie Ihr Wörterbuch als <String, Either<String, NSNumber>>.

Oder wenn Sie Fan von hartzucodieren Typen sind:

enum StringOrNumber { 
    case string(String) 
    case number(NSNumber) 

    init(string: String) { 
     self = .string(string) 
    } 

    init(number: NSNumber) { 
     self = .number(number) 
    } 

    init?(any: Any) { 
     if let string = any as? String { 
      self = .string(string) 
     } else if let number = any as? NSNumber { 
      self = .number(number) 
     } else { 
      return nil 
     } 
    } 
} 

Und Wörterbuch würde <String, StringOrNumber>

EDIT sein: Wenn Ihr Wörterbuch selbst nur alle enthalten kann entweder alleString s oder NSNumber s als Wert, Typ sollte Either<[String: String], [String: NSNumber]> sein

+0

Mache das optionale init 'private' für ein gutes Mass ...? – Grimxn

+0

Das Problem damit ist, dass er immer noch nicht 'var stringOrNumberDict deklarieren kann: [String: Entweder ] = [" A ": 1," B ":" zwei "]' er muss '[" A "verwenden: Entweder (1), "B": Entweder ("zwei")] '. Gute Arbeit, obwohl! – Grimxn

+0

@Grimxn, warum versteckst du 'init? (: Any)'? Es ist für den Fall, wenn Sie eine '[Any]' mit 'Strings' /' NSNumber' haben und Sie "map" darüber, um Array von entweder-enum zu erhalten. – user28434

-1

Kann Ihnen das helfen?

func myFoo<T>(dic: [String: T]) { 

    if dic["Key"] is String { 
     print("The value is String --> \(dic["Key"])") 
    } 

    if dic["Key"] is Int { 
     print("The value is Int --> \(dic["Key"])") 
    } 
} 

myFoo(dic: ["Key": 0]) 
myFoo(dic: ["Key": "Value"]) 
0

Erweiterung auf user28434 ‚s Antwort, könnten Sie die C Union-Typ Verhalten nachahmen, da sie die‚entweder oder‘Situation am besten dient und hat sich gegenseitig ausschließende Fälle.

rasche Umsetzung einer Union könnte allerdings hässlich aussehen:

enum StringOrNum { 
    case a(String) 
    case b(NSNumber) 

    var a: String { 
     switch(self) 
     { 
     case .a(let stringValue): return stringValue 
     case .b(let nsnumberValue): return String(format:"%f", nsnumberValue.doubleValue) 
     } 
    } 

    var b: NSNumber { 
     switch(self) 
     { 
     case .a(let stringValue): if let intValue = Int(stringValue) { 
             return NSNumber(value: intValue) 
            } 
            else { 
             return NSNumber(value: 0) // default case 
            } 
     case .b(let nsnumberValue): return nsnumberValue 
     } 
    } 
} 

dann das Wörterbuch erklären als Dictionary<String, StringOrNum>

Verwandte Themen