Quiero crear un Dictionary que no limita el tipo de clave (como NSDictionary)

Así que he intentado

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, voy a utilizar 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>()
           ^~~~~~~~~~~~~~~~~~~~~~~~~~~

Así Hashable heredado de Equatable pero no se ajusta a Equatable??? No entiendo…

De todos modos, tratando de mantener la

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 compilador feliz 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 no es realmente lo que quiero.

  • hacer una clase que se ajusta a Hashable y Equatable?
  • ¿es útil? se limita el tipo de clave para ser esa clase. y si usted realmente quiere decir hacer una nueva protocol, que también no ayuda. porque no puedo extensiones de cada una de las otras clases para hacerlas cumplir mi protocolo
  • así de una forma u otra tienes que convencer Diccionario que su genérico tipo de clave puede ser tanto de hash y comparar la igualdad, ya que es como un diccionario de obras
  • de lo contrario, se podría implementar su propio diccionario con su propio hash/comparación de métodos
  • no tienen idea acerca de cómo genérico puede ayudar. y estoy luchando con el tipo de corrector, incluso con mi propio diccionario de la implementación, todavía tienen el mismo problema. Debido a que el tipo de corrector que va a hacer lo mismo a mí otra vez en el nuevo tipo de diccionario.
  • Hay una razón AnyObject no se ajusta a Hashable y Equatable? Hay un conocido subtipo de AnyObject que no haría bien con esto?
  • ¿a qué te refieres? AnyObject es @class_protocol protocol AnyObject {}, que, por definición, no se ajusta a ninguna de protocolo
  • A la derecha, yo sé que no, yo estaba solo (filosóficamente) pensando en lo que sería el mundo si no lo hizo, así: @class_protocol protocol BetterAnyObject : NSObjectProtocol, Equatable, Hashable {}
  • eso es básicamente NSObject
  • Creo que podría ser la respuesta a su pregunta – si un Dictionary requiere que sus claves tanto equatable y hashable, y NSObject es el tipo más genérico que implementa tanto, que es lo que usted necesita para utilizar como su clave. (Aunque sé que usted menciona no querer usar ese, parece ser que la única opción con lo que se dispone en la actualidad.)
  • bueno, es la solución de reserva. que me deje poner struct y enum en el diccionario.
  • Lo cual tiene sentido. Si el compilador no puede garantizar que un enum será capaz de proporcionar un hash en tiempo de ejecución, y la Dictionary requiere que una de sus claves, no se puede usar enum tipos de llaves. Como otro comentarista menciona, su mejor opción podría ser una subclase personalizada que soporta enum/struct claves.
  • Supongo que puede tener la clase de contenedor para mantener la enumeración y estructura. pero el compilador puede garantizar porque puedo hacer enum(y struct) conformar a la Hashable y si el tipo de sistema funciona como yo esperaba, debería aceptar cualquier cosa que se ajusten a Hashable (class, struct y enum)
  • Me tomé la libertad de cross-posting / enlace a esta pregunta en un post aparte en el Apple Dev foros y esta pregunta es respondida aquí: devforums.apple.com/message/1045616 .
  • Uso NSMutableDictionary

InformationsquelleAutor Bryan Chen | 2014-06-09

8 Comentarios

  1. 58

    Swift 3 actualización

    Ahora puede utilizar AnyHashable que es un tipo de borrado hashable valor, creada precisamente para situaciones como esta:

    var dict = Dictionary<AnyHashable, Int>()

    • Este debe ser el nuevo aceptado respuesta.
    • Esta es la respuesta..!
  2. 16

    Creo que, como de Swift 1.2, se puede usar un ObjectIdentifier struct para esto. Se implementa Hashable (y por lo tanto Equatable) así como Comparables. Se puede utilizar para envolver cualquier instancia de la clase. Supongo que la aplicación utiliza el objeto envuelto subyacente de la dirección de la hashValue, así como en el operador==.

    • Esta es la respuesta correcta a esta pregunta, no estoy seguro, ¿por qué no está marcado/votado como tal
    • Excelente respuesta
  3. 11

    Me tomé la libertad de cross-posting /enlace a esta pregunta en un post aparte en el Apple Dev foros y esta pregunta es la respuesta aquí.

    Editar

    Esta respuesta desde el 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)
    }
    • Esto funcionó para mí, pero he tenido que añadir una Tecla(«cadena») en el sitio de llamada. Algo para tener en cuenta si estás diseñando una interfaz pública.
    • Aquellos que no están familiarizados con los de arriba «Tipo de Borrado» técnica disfrutar de @rob-napier a hablar: youtube.com/watch?v=QCxkaTj7QJs
  4. 7

    Diccionario es struct Dictionary<Key : Hashable, Value>...
    Lo que significa que su Valor podría ser cualquier cosa que usted desea, y la Clave puede ser de cualquier tipo que desee, pero debe ajustarse a Hashable protocolo.

    Usted no puede crear Dictionary<Any, Int>() o Dictionary<AnyObject, Int>(), porque Any y AnyObject no puede garantizar que dicha Clave se ajusta Hashable

    Usted no puede crear Dictionary<Hashable, Int>(), porque Hashable no es un tipo que se acaba de protocolo, que es necesario describir el tipo.

    Así Hashable heredado de Equatable pero no se ajusta a
    Equatable??? No entiendo…

    Pero estás equivocado en cuanto a la terminología. Original de error es

    type 'Hashable' does not conform to inherited protocol 'Equatable.Protocol'
    Eso significa que Xcode suponiendo que ‘Hashable’ de algún tipo, pero no es ningún tipo. Y Xcode tratar como una especie de texto vacíos, que, obviamente, no se conforma con cualquier protocolo en todos (en este caso no se ajusta a heredado protocolo Equatable)

    Algo similar sucede con la KeyType.

    Un alias de tipo de declaración introduce un nombre de alias de un tipo existente en el programa.

    Ver existing type. protocol<Hashable, Equatable> no es un tipo de protocolo con el fin de Xcode de nuevo le dice que type 'KeyType' does not conform to protocol 'Equatable'

    Puede utilizar Dictionary<NSObject, Int> simplemente, porque NSObject cumple Hashable protocolo.

    Swift es inflexible de lengua y usted no puede hacer algunas cosas, como creating Dictionary that can hold anything in Key. En realidad diccionario ya es compatible con cualquier puede mantener nada en Clave, que se ajusta Hashable. Pero ya que usted debe especificar la clase en particular que usted no puede hacer esto para los nativos de Swift clases, porque no hay tal maestro de la clase en Swift como en Objective-C, el cual se conforma de aire puede ajustarse (con una ayuda de extensiones) Hashable

    Por supuesto, usted puede utilizar un envoltorio como chrisco sugerido. Pero realmente no puedo imaginar por qué usted lo necesita. Es genial que tiene tipado fuerte en Swift, así que usted no necesita preocuparse acerca de los tipos de fundición como lo hizo en Objective-C

  5. 2

    Hashable es sólo un protocolo de modo que no puede especificar directamente como tipo para la Key valor. Lo que realmente se necesita es una forma de expresar «cualquier tipo de T, tal que T implementa Hashable. Esto es manejado por tipo de limitaciones en Swift:

    func makeDict<T: Hashable>(arr: T[]) {
      let x = Dictionary<T, Int>()
    }

    Este código se compila.

    AFAIK, sólo puede utilizar restricciones de tipo genérico de funciones y clases.

    • Por alguna razón que no puedo especificar Hashable directamente como un tipo de la Clave? Que puedo hacer para valor. y mi objetivo es crear diccionario que la clave puede ser Int y String y nada se ajustan a Hashable
    • Esto funcionará para cualquier T que implementa Hashable, que incluye Int y String.
    • pero sólo funciona para Int o de la Cadena. Quiero Int y String
    • Eso es bastante inusual de casos de uso. Puede usted explicar?
    • eso es lo que NSDictioanry puede hacer, el tipo de clave sólo necesitan cumplir NSCopying que puede ser NSString o NSNumber
    • Sí, pero normalmente se utiliza con una clave de un solo tipo. ¿Cuál es la razón fundamental de la necesidad de esta estructura de datos?
    • principalmente porque quiero saber cómo hacerlo en swift. y puede ser útil para la biblioteca de código de aceptar las diferentes definidas por el tipo de clave
    • A quién le importa lo que la razón subyacente es. El punto es que Bryan ha encontrado una bastante importante limitación en Swift que Obj-C no tiene. Yo estoy en un problema similar yo con medicamentos genéricos en Swift. Casi cualquier otro idioma podría hacer esto con los Protocolos o Interfaces. Swift de la aplicación es muy pobre aquí.
    • Es bastante común que pedir un cartel de contexto y de un nivel superior en la descripción del problema. Esto me parece algo poco habitual, por lo que pidió más información.
    • Pido disculpas por mi tono. Por supuesto, pidiendo una aclaración está muy bien; sonaba como si estuviera defendiendo a la limitación de no considerar su razón válida. Tbh estoy muy frustrado con Swift limitaciones, aunque obviamente me doy cuenta de que es aún preliminar.
    • Quiero que esta funcionalidad de seguir la pista o los resultados de los diferentes objetos sin tener que hacer que los objetos se ajusten a Hashable, ver más abajo para mi solución.

  6. 2

    Esto no es exactamente la respuesta a la pregunta, pero me ha ayudado.

    La respuesta general sería implementar Hashable para todos sus tipos, sin embargo, que puede ser difícil para los Protocolos porque Hashable se extiende Equatable y Equatable utiliza Auto que impone severas limitaciones en lo que un protocolo se puede utilizar para.

    Implementar en su lugar imprimir y, a continuación, hacer:

    var dict = [String: Int]
    dict[key.description] = 3

    La implementación de la descripción tiene que ser algo como:

    var description : String {
        return "<TypeName>[\(<Field1>), \(<Field2>), ...]"
    }

    No una respuesta perfecta, pero la mejor que tengo hasta ahora 🙁

    • Gracias por esto, me ayudó a entender cómo resolver mi problema en particular, que he publicado como respuesta a continuación.
  7. 0

    Esto no responde a la OP de la pregunta, pero es algo relacionado, y puede que esperamos sean de uso para algunas situaciones. Supongamos que lo que realmente quiero hacer es esto:

       public var classTypeToClassNumber = [Any.Type : Int]()

    Pero Swift está diciendo «Tipo» Todo.Tipo’ no se ajusta al protocolo de Hashable».

    De la mayoría de las respuestas sobre el uso de instancias de objetos como clave de un diccionario, no se utiliza el tipo de objeto. (Que es bastante justo, que es lo que el OP estaba preguntando acerca.) Fue la respuesta por Howard Lovatt que me llevó a una solución que se puede usar.

    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
    }
    }

    EDICIÓN:

    Si las clases participantes están definidas en módulos diferentes, y puede que tengan conflicto de nombres de clase si se descuida el nombre del módulo, a continuación, sustituir «de la Cadena(que refleja:» para la «Cadena(de describir:», tanto a la hora de diseñar el diccionario y, al hacer la búsqueda.

  8. -2

    Puede utilizar el nombre de la clase como un Hashable, por ejemplo:

    var dict = [String: Int]
    dict[object_getClassName("key")] = 3

    Ver ¿Cómo puedo imprimir el tipo o clase de una variable en Swift? para que puedan obtener el nombre de la clase.

    • No tengo idea de cómo se hace esto soluciona nada
    • Uy re-leer la pregunta siguiente el comentario, no resuelve la cuestión. Se resuelve la pregunta sólo si cada objeto de que el diccionario es un tipo diferente. Este fue mi caso de uso y por lo tanto resuelto mi problema, sin embargo no es la pregunta, lo siento.

Dejar respuesta

Please enter your comment!
Please enter your name here