sport - suzuki swift boosterjet
¿Cómo crear Diccionario que pueda contener cualquier cosa en Key? o todo el tipo posible que sea capaz de contener (8)
Quiero crear un Dictionary
que no limite el tipo de clave (como NSDictionary
)
Así que lo intenté
var dict = Dictionary<Any, Int>()
y
var dict = Dictionary<AnyObject, Int>()
resultante
error: type ''Any'' does not conform to protocol ''Hashable''
var dict = Dictionary<Any, Int>()
^
<REPL>:5:12: error: cannot convert the expression''s type ''<<error type>>'' to type ''$T1''
var dict = Dictionary<Any, Int>()
^~~~~~~~~~~~~~~~~~~~~~
OK, Hashable
var dict = Dictionary<Hashable, Int>()
pero
error: type ''Hashable'' does not conform to protocol ''Equatable''
var dict = Dictionary<Hashable, Int>()
^
Swift.Equatable:2:8: note: ''=='' requirement refers to ''Self'' type
func ==(lhs: Self, rhs: Self) -> Bool
^
Swift.Hashable:1:10: note: type ''Hashable'' does not conform to inherited protocol ''Equatable.Protocol''
protocol Hashable : Equatable
^
<REPL>:5:12: error: cannot convert the expression''s type ''<<error type>>'' to type ''$T1''
var dict = Dictionary<Hashable, Int>()
^~~~~~~~~~~~~~~~~~~~~~~~~~~
¿ Hashable
heredado de Equatable
pero no se ajusta a Equatable
? No entiendo...
De todos modos, sigue intentando
typealias KeyType = protocol<Hashable, Equatable> // KeyType is both Hashable and Equatable
var dict = Dictionary<KeyType, Int>() // now you happy?
sin suerte
error: type ''KeyType'' does not conform to protocol ''Equatable''
var dict = Dictionary<KeyType, Int>()
^
Swift.Equatable:2:8: note: ''=='' requirement refers to ''Self'' type
func ==(lhs: Self, rhs: Self) -> Bool
^
Swift.Hashable:1:10: note: type ''KeyType'' does not conform to inherited protocol ''Equatable.Protocol''
protocol Hashable : Equatable
^
<REPL>:6:12: error: cannot convert the expression''s type ''<<error type>>'' to type ''$T1''
var dict = Dictionary<KeyType, Int>()
^~~~~~~~~~~~~~~~~~~~~~~~~~
Estoy tan perdido ahora, ¿cómo puedo hacer que el compilador esté contento con mi código?
Quiero usar el diccionario como
var dict = Dictionary<Any, Int>()
dict[1] = 2
dict["key"] = 3
dict[SomeEnum.SomeValue] = 4
Sé que puedo usar Dictionary<NSObject, Int>
, pero en realidad no es lo que quiero.
Swift 3 actualización
Ahora puede usar AnyHashable
que es un valor hashable borrado por tipo, creado exactamente para escenarios como este:
var dict = Dictionary<AnyHashable, Int>()
Creo que, a partir de Swift 1.2, puede usar una estructura ObjectIdentifier para esto. Implementa Hashable (y por lo tanto Equatable) así como Comparable. Puede usarlo para envolver cualquier instancia de clase. Supongo que la implementación usa la dirección subyacente del objeto envuelto para hashValue, así como dentro del operador ==.
El diccionario es struct Dictionary<Key : Hashable, Value>...
Lo que significa que Value puede ser cualquier cosa que desee, y Key podría ser del tipo que desee, pero Key debe cumplir con el protocolo Hashable
.
No puede crear Dictionary<Any, Int>()
o Dictionary<AnyObject, Int>()
, porque Any
y AnyObject
no pueden garantizar que dicha clave se ajuste a Hashable
No puede crear Dictionary<Hashable, Int>()
, porque Hashable
no es un tipo, solo es un protocolo que describe el tipo necesario.
¿Hashable heredado de Equatable pero no se ajusta a Equatable? No entiendo...
Pero te equivocas en terminología. Error original es
type ''Hashable'' does not conform to inherited protocol ''Equatable.Protocol''
Esto significa que Xcode asume ''Hashable'' como algún tipo, pero no existe tal tipo. Y Xcode lo trata como un tipo de tipo vacío, que obviamente no concuerda con ningún protocolo (en este caso no cumple con el protocolo heredado Equatable
)
Algo similar ocurre con KeyType
.
Una declaración de alias de tipo introduce un alias con nombre de un tipo existente en su programa.
Ves existing type
. protocol<Hashable, Equatable>
no es un tipo, es un protocolo, por lo que Xcode vuelve a decirle que el type ''KeyType'' does not conform to protocol ''Equatable''
Puede usar Dictionary<NSObject, Int>
just, porque NSObject
cumple Hashable
protocolo Hashable
.
Swift es un lenguaje fuerte de tipeo y no puedes hacer cosas como creating Dictionary that can hold anything in Key
. En realidad, el diccionario ya admite que cualquiera puede contener cualquier cosa en Key, que se ajusta a Hashable
. Pero dado que debe especificar una clase particular, no puede hacer esto para las clases Swift nativas, porque no existe tal clase maestra en Swift como en Objective-C, que puede adaptarse según el aire (con la ayuda de extensiones) a Hashable
Por supuesto, puedes usar una envoltura como la sugerida por chrisco
. Pero realmente no puedo imaginar por qué lo necesitas. Es genial que tengas un tipeo fuerte en Swift para que no tengas que preocuparte por la conversión de tipos como lo hiciste en Objective-C
Esto no responde a la pregunta del OP, pero está relacionado de alguna manera y, con suerte, puede ser útil para algunas situaciones. Supongamos que lo que realmente quieres hacer es esto:
public var classTypeToClassNumber = [Any.Type : Int]()
Pero Swift te está diciendo que "Tipo" Any.Type "no se ajusta al protocolo Hashable".
La mayoría de las respuestas anteriores se refieren al uso de instancias de objetos como una clave de diccionario, sin utilizar el tipo de objeto. (Lo cual es justo, eso es lo que el OP estaba preguntando). Fue la respuesta de Howard Lovatt la que me llevó a una solución utilizable.
public class ClassNumberVsClassType {
public var classTypeToClassNumber = [String : Int]()
public init() {
classTypeToClassNumber[String(describing: ClassWithStringKey.self)] = 367622
classTypeToClassNumber[String(describing: ClassBasedOnKeyedItemList3.self)] = 367629
classTypeToClassNumber[String(describing: ClassBasedOnKeyedItemList2.self)] = 367626
classTypeToClassNumber[String(describing: ClassWithGuidKey.self)] = 367623
classTypeToClassNumber[String(describing: SimpleStruct.self)] = 367619
classTypeToClassNumber[String(describing: TestData.self)] = 367627
classTypeToClassNumber[String(describing: ETestEnum.self)] = 367617
classTypeToClassNumber[String(describing: ClassBasedOnKeyedItemList0.self)] = 367624
classTypeToClassNumber[String(describing: ClassBasedOnKeyedItemList1.self)] = 367625
classTypeToClassNumber[String(describing: SimpleClass.self)] = 367620
classTypeToClassNumber[String(describing: DerivedClass.self)] = 367621
}
public func findClassNumber(_ theType : Any.Type) -> Int {
var s = String(describing: theType)
if s.hasSuffix(".Type") {
s = s.substring(to: s.index(s.endIndex, offsetBy: -5)) // Remove ".Type"
}
let classNumber = _classTypeToClassNumber[s]
return classNumber != nil ? classNumber! : -1
}
}
EDITAR:
Si las clases implicadas están definidas en módulos diferentes, y pueden tener nombres de clases conflictivos si descuida el nombre del módulo, entonces sustituya "Cadena (reflejando:" por "Cadena (describiendo:"), tanto al compilar el diccionario como al realizar la búsqueda .
Esto no responde exactamente la pregunta, pero me ha ayudado.
La respuesta general sería implementar Hashable para todos sus tipos, sin embargo, eso puede ser difícil para los Protocolos porque Hashable extiende Self y Equatable usa Self que impone limitaciones severas sobre para qué se puede usar un protocolo.
En su lugar, implemente Imprimible y luego haga lo siguiente:
var dict = [String: Int]
dict[key.description] = 3
La implementación de la descripción tiene que ser algo así como:
var description : String {
return "<TypeName>[/(<Field1>), /(<Field2>), ...]"
}
No es una respuesta perfecta, pero la mejor que tengo hasta ahora :(
Me tomé la libertad de realizar publicaciones cruzadas / enlaces a esta pregunta en una publicación separada en los foros de Apple Dev y esta pregunta se devforums.apple.com/message/1045616 .
Editar
Esta respuesta del enlace de arriba funciona en 6.1 y mayor:
struct AnyKey: Hashable {
private let underlying: Any
private let hashValueFunc: () -> Int
private let equalityFunc: (Any) -> Bool
init<T: Hashable>(_ key: T) {
underlying = key
// Capture the key''s hashability and equatability using closures.
// The Key shares the hash of the underlying value.
hashValueFunc = { key.hashValue }
// The Key is equal to a Key of the same underlying type,
// whose underlying value is "==" to ours.
equalityFunc = {
if let other = $0 as? T {
return key == other
}
return false
}
}
var hashValue: Int { return hashValueFunc() }
}
func ==(x: AnyKey, y: AnyKey) -> Bool {
return x.equalityFunc(y.underlying)
}
Puede usar el nombre de la clase como Hashable, por ejemplo:
var dict = [String: Int]
dict[object_getClassName("key")] = 3
Consulte ¿Cómo imprimo el tipo o clase de una variable en Swift? para saber cómo puedes obtener el nombre de la clase.
Hashable
es solo un protocolo, por lo que no puede especificarlo directamente como un tipo para el valor Key
. Lo que realmente necesita es una forma de expresar "cualquier tipo T, tal que T implemente Hashable
. Esto se maneja mediante restricciones de tipo en Swift:
func makeDict<T: Hashable>(arr: T[]) {
let x = Dictionary<T, Int>()
}
Este código compila
AFAIK, solo puedes usar restricciones de tipo en las clases y funciones genéricas.