Digitare il controllo e la validation in Swift

Inizialmente ho postato questa domanda come modifica successiva di questa domanda qui , ma sembra sufficientemente distinta da farla da sola. Quindi eccolo qui.

Sto cercando di verificare che i valori di un dictionary [String: Any] siano del tipo che mi aspetto. Quello che mi piacerebbe fare è qualcosa del genere:

 func objectIsType<T>(object: Any, someObjectOfType: T.Type) -> Bool { return object is T } let validator: [String: Any.Type] = [ "gimme an int": Int.self, "this better be a string": String.self ] let validatee: [String: Any] = [ "gimme an int": 3, "this better be a string": "it is!" ] for (key, type) in validator { if !objectIsType(validatee[key], type) { selfDestruct() } } 

Ma sto ricevendo l'errore, <>protocol.Type is not convertible to T.type. Se, invece di usare la function objectIsType , la uso is direttamente, creando il loop

 for (key, type) in validator { if !(validatee[key] is type) { selfDestruct() } } 

Ottengo l'errore, 'type' is not a type . Ho esaminato la documentazione di Swift Metatype, ma sono ancora un po 'confuso. C'è un buon modo per fare questo tipo di cose?

Solutions Collecting From Web of "Digitare il controllo e la validation in Swift"

Ho paura che questo approccio non funzioni. Il motivo è che stai cercando di mescolare il comportmento dinamico in fase di esecuzione con controlli statici in fase di compilazione.

La function:

 func objectIsType<T>(object: Any, someObjectOfType: T.Type) -> Bool { return object is T } 

sta usando una specie di trucco / trucco per fare qualcosa che a Swift non piace fare, che sta specificando il tipo per un segnaposto generico senza context. Cosa significa? Beh, davvero, quello che volevi fare è questo:

 func objectIsType<T>(obj: Any) -> Book { return obj is T } 

e quindi chiamalo così:

 // See if myAny is an Integer... objectIsType<Int>(myAny) 

Ma in Swift (diversamente da, per esempio, C ++), non puoi farlo. Non puoi dire al compilatore "usa Int per il segnaposto T ". Swift ti permetterà di determinare T dal context in cui lo stai chiamando. Quindi, il trucco che usi è correggere T con altri mezzi. Qui, stai usando il metatipo del tipo. Un'alternativa sarebbe quella di fornire un valore fittizio che non viene utilizzato (come nella domanda originale).

 func objectIsType<T>(object: Any, dummyValOfThatType: T) -> Bool { // no use of the dummy value is made... return object is T } // then, to detect Ints, supply a dummy Int objectIsType(myAny, 1) 

Ovviamente il recupero di valori fittizi è un po 'spazzatura, quindi la soluzione metatype si sente meglio. Sfortunatamente, si dà l'illusione che esista una variabile che può essere usata per riferirsi a un tipo di runtime e può quindi essere usata con is o as . Ma questo è solo questo, un'illusione. Non puoi farlo con i tipi base di Swift. Semplicemente non lo supportno.

Tieni a mente che cosa succede quando chiami una function generica mentre in fase di compilazione Swift ti scrive una versione della function necessaria per il tipo con cui la stai usando. Quindi, quando fornisci un Int.self , ciò che effettivamente accade è che il compilatore ti scrive questa function:

 objectIsType(obj: Any, someObjectOfType: Int.Type) -> Bool { return obj is Int } 

Non posso sottolineare abbastanza, lo fa al momento della compilazione . Quindi non c'è modo che questa function possa essere chiamata con una varietà di tipi diversi determinati in fase di esecuzione e farlo funzionare. Invece, quando dichiari i tuoi dizionari, stai dichiarando un dictionary pieno di valori di Any.Type . Poiché Any.Type è un tipo super di tutti gli altri tipi, puoi comunque assegnare String.Type , ma in realtà tutto ciò che verrà archiviato è Any.Type . Il protocol<>.Type è in realtà un altro modo di dire Any.Type perché questo è ciò che Any è: è definito come typealias Any = protocol<> . Anche se il tuo codice è stato compilato, verrebbe sempre restituito come richiesto, se objectIsType(Any, Any.Type)

(Non sono del tutto sicuro del perché non lo farà, a parte questo è necessario scartare il valore che si ottiene dal dictionary dato che è facoltativo, ma questo dovrebbe farlo: if let obj = validatee[key] where !objectIsType(obj, type.self) { selfDestruct() } )

Fondamentalmente, se vuoi questo tipo di comportmento di tipo dinamico di runtime, probabilmente dovresti rimanere con @objc land, a less che qualcun altro non conosca un buon trucco.

Tuttavia , questo è il punto in cui chiedo: cosa stai cercando di get? Hai davvero bisogno di questo tipo di comportmento dinamico? Molto spesso, puoi riformulare il tuo objective reale in termini di generici in fase di compilazione invece di andare in giro con la conversione di Any . Non sempre, ma spesso.