programing

Key에 무엇이든 담을 수있는 Dictionary를 만드는 방법?

goodcopy 2021. 1. 14. 23:14
반응형

Key에 무엇이든 담을 수있는 Dictionary를 만드는 방법? 또는 보유 할 수있는 모든 가능한 유형


Dictionary키 유형을 제한하지 않는 을 만들고 싶습니다 (예 NSDictionary:)

그래서 나는 시도했다

var dict = Dictionary<Any, Int>()

var dict = Dictionary<AnyObject, Int>()

결과

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

네, 사용 할게요 Hashable

var dict = Dictionary<Hashable, Int>()

그러나

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상속 Equatable되었지만 준수하지 않는 Equatable??? 이해가 안 돼요 ...

어쨌든 계속 노력해

typealias KeyType = protocol<Hashable, Equatable> // KeyType is both Hashable and Equatable
var dict = Dictionary<KeyType, Int>() // now you happy?

운없이

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

나는 지금 너무 길을 잃었다. 어떻게 내 코드로 컴파일러를 만족시킬 수 있을까?


다음과 같이 사전을 사용하고 싶습니다.

var dict = Dictionary<Any, Int>()
dict[1] = 2
dict["key"] = 3
dict[SomeEnum.SomeValue] = 4

사용할 수 있다는 것을 알고 Dictionary<NSObject, Int>있지만 실제로 원하는 것은 아닙니다.


Swift 3 업데이트

이제 AnyHashable다음과 같은 시나리오를 위해 정확하게 생성 된 유형이 지워진 해시 가능 값을 사용할 수 있습니다 .

var dict = Dictionary<AnyHashable, Int>()


저는 Swift 1.2부터이를 위해 ObjectIdentifier 구조체를 사용할 수 있다고 믿습니다. Hashable (따라서 Equatable)과 Comparable을 구현합니다. 클래스 인스턴스를 래핑하는 데 사용할 수 있습니다. 구현이 == 연산자 내 에서뿐만 아니라 hashValue에 대해 래핑 된 개체의 기본 주소를 사용한다고 생각합니다.


나는 Apple Dev 포럼의 별도 게시물에서이 질문에 대한 교차 게시 / 링크의 자유를 얻었으며이 질문에 대한 답변은 여기에 있습니다 .

편집하다

위 링크의이 답변은 6.1 이상에서 작동합니다.

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

Dictionary is struct Dictionary<Key : Hashable, Value>...which는 Value가 원하는 모든 것이 될 수 있고 Key는 원하는 모든 유형이 될 수 있지만 Key는 Hashable프로토콜을 준수해야 함을 의미 합니다.

You can't create Dictionary<Any, Int>() or Dictionary<AnyObject, Int>(), because Any and AnyObject can't guarantee that such a Key conforms Hashable

You can't create Dictionary<Hashable, Int>(), because Hashable is not a type it is just protocol which is describing needed type.

So Hashable inherited from Equatable but it does not conform to Equatable??? I don't understand...

But you are wrong in terminology. Original error is

type 'Hashable' does not conform to inherited protocol 'Equatable.Protocol' That means that Xcode assuming 'Hashable' as some type, but there is no such type. And Xcode treat it as some kind empty type, which obviously does not conform any protocol at all (in this case it does not conform to inherited protocol Equatable)

Something similar happens with KeyType.

A type alias declaration introduces a named alias of an existing type into your program.

You see existing type. protocol<Hashable, Equatable> is not a type it is protocol so Xcode again tells you that type 'KeyType' does not conform to protocol 'Equatable'

You can use Dictionary<NSObject, Int> just, because NSObject conforms Hashable protocol.

Swift is strong typing language and you can't do some things like creating Dictionary that can hold anything in Key. Actually dictionary already supports any can hold anything in Key, which conforms Hashable. But since you should specify particular class you can't do this for native Swift classes, because there is no such master class in Swift like in Objective-C, which conforms air could conform (with a help of extensions) to Hashable

Of course you can use some wrapper like chrisco suggested. But I really can't imagine why you need it. It is great that you have strong typing in Swift so you don't need to worry about types casting as you did in Objective-C


Hashable is just a protocol so you can't specify it directly as a type for the Key value. What you really need is a way of expressing "any type T, such that T implements Hashable. This is handled by type constraints in Swift:

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

This code compiles.

AFAIK, you can only use type constraints on generic functions and classes.


This doesn't exactly answer the question, but has helped me.

The general answer would be implement Hashable for all your types, however that can be hard for Protocols because Hashable extends Equatable and Equatable uses Self which imposes severe limitations on what a protocol can be used for.

Instead implement Printable and then do:

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

The implementation of description has to be something like:

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

Not a perfect answer, but the best I have so far :(


This does not answer the OP's question, but is somewhat related, and may hopefully be of use for some situations. Suppose that what you really want to do is this:

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

But Swift is telling you "Type 'Any.Type' does not conform to protocol Hashable".

Most of the above answers are about using object instances as a dictionary key, not using the type of the object. (Which is fair enough, that's what the OP was asking about.) It was the answer by Howard Lovatt that led me to a usable solution.

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

EDIT:

If the classes involved are defined in different modules, and may have conflicting class names if you neglect the module name, then substitute "String(reflecting:" for "String(describing:", both when building up the dictionary and when doing the lookup.


You can use the class name as a Hashable, e.g.:

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

See How do I print the type or class of a variable in Swift? for how you might get the class name.

ReferenceURL : https://stackoverflow.com/questions/24119624/how-to-create-dictionary-that-can-hold-anything-in-key-or-all-the-possible-type

반응형