制作一个 Swift 字典,其中键是“类型"? [英] Make a Swift dictionary where the key is "Type"?

查看:25
本文介绍了制作一个 Swift 字典,其中键是“类型"?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试做这种事情..

I'm trying to do this sort of thing ..

static var recycle: [Type: [CellThing]] = []

但是 - 我不能:)

未声明的类型类型"

在示例中,CellThing 是我的基类,所以 A:CellThingB:CellThingC:CellThing 等等.这个想法是我将在字典数组中存储各种 A A A, B B, C C C C.

In the example, CellThing is my base class, so A:CellThing, B:CellThing, C:CellThing and so on. The idea is I would store various A A A, B B, C C C C in the dictionary arrays.

如何使类型"(我猜理想情况下仅限于 CellThing)成为 Swift 字典中的键?

How to make a "Type" (ideally I guess, constrained to CellThing) be the key in a Swift dictionary?

我很感激我可以(也许?)使用 String(describing: T.self),但这会让我失眠.

I appreciate I could (perhaps?) use String(describing: T.self), but that would make me lose sleep.

这是一个用例,设想的代码看起来像这样......

Here's a use case, envisaged code would look something like this ...

@discardableResult class func make(...)->Self {
  return makeHelper(...)
  }

private class func makeHelper<T: CellThing>(...)->T {
  let c = instantiateViewController(...) as! T
  return c
  }

那么像......

static var recycle: [Type: [CellThing]] = []

private class func makeHelper<T: CellThing>(...)->T {
  let c = instantiateViewController(...) as! T

  let t = type whatever of c (so, maybe "A" or "B")
  recycle[t].append( c )

  let k = recycle[t].count
  print wow, you have k of those already!

  return c
  }

推荐答案

不幸的是,目前元类型类型无法符合协议(参见 关于此事的相关问题) – 所以CellThing.Type 目前不、也不能符合Hashable.因此,这意味着它不能直接用作 DictionaryKey.

Unfortunately, it's currently not possible for metatype types to conform to protocols (see this related question on the matter) – so CellThing.Type does not, and cannot, currently conform to Hashable. This therefore means that it cannot be used directly as the Key of a Dictionary.

但是,您可以使用 ObjectIdentifier 为元类型创建包装器 以提供 Hashable 实现.例如:

However, you can create a wrapper for a metatype, using ObjectIdentifier in order to provide the Hashable implementation. For example:

/// Hashable wrapper for a metatype value.
struct HashableType<T> : Hashable {

  static func == (lhs: HashableType, rhs: HashableType) -> Bool {
    return lhs.base == rhs.base
  }

  let base: T.Type

  init(_ base: T.Type) {
    self.base = base
  }

  func hash(into hasher: inout Hasher) {
    hasher.combine(ObjectIdentifier(base))
  }
  // Pre Swift 4.2:
  // var hashValue: Int { return ObjectIdentifier(base).hashValue }
}

然后,您还可以在 Dictionary 上提供一个方便的下标,它接受一个元类型并将其包装在 HashableType 中:

You can then also provide a convenience subscript on Dictionary that takes a metatype and wraps it in a HashableType for you:

extension Dictionary {
  subscript<T>(key: T.Type) -> Value? where Key == HashableType<T> {
    get { return self[HashableType(key)] }
    set { self[HashableType(key)] = newValue }
  }
}

然后可以像这样使用:

class CellThing {}
class A : CellThing {}
class B : CellThing {}

var recycle: [HashableType<CellThing>: [CellThing]] = [:]

recycle[A.self] = [A(), A(), A()]
recycle[B.self] = [B(), B()]

print(recycle[A.self]!) // [A, A, A]
print(recycle[B.self]!) // [B, B]

这也适用于泛型,您只需用 T.self 代替字典即可.

This should also work fine for generics, you would simply subscript your dictionary with T.self instead.

不幸的是,在此处使用带有 getset 的下标的一个缺点是,在使用复制的字典值时,您将导致性能下降 -编写诸如 Array 之类的类型(例如在您的示例中).我在本问答中更多地讨论了这个问题.

Unfortunately one disadvantage of using a subscript with a get and set here is that you'll incur a performance hit when working with dictionary values that are copy-on-write types such as Array (such as in your example). I talk about this issue more in this Q&A.

一个简单的操作,如:

recycle[A.self]?.append(A())

将触发存储在字典中的数组的 O(N) 副本.

will trigger an O(N) copy of the array stored within the dictionary.

这是一个旨在通过 通用访问器,已在 Swift 5 中作为非官方语言功能实现.如果您习惯使用可能在未来版本中损坏的非官方语言功能(不真正推荐用于生产)代码),那么你可以将下标实现为:

This is a problem that is aimed to be solved with generalised accessors, which have been implemented as an unofficial language feature in Swift 5. If you are comfortable using an unofficial language feature that could break in a future version (not really recommended for production code), then you could implement the subscript as:

extension Dictionary {
  subscript<T>(key: T.Type) -> Value? where Key == HashableType<T> {
    get { return self[HashableType(key)] }
    _modify {
      yield &self[HashableType(key)]
    }
  }
}

解决了性能问题,允许在字典中就地改变数组值.

which solves the performance problem, allowing an array value to be mutated in-place within the dictionary.

否则,一个简单的替代方法是不定义自定义下标,而只是在您的类型上添加一个方便的计算属性,让您将其用作键:

Otherwise, a simple alternative is to not define a custom subscript, and instead just add a convenience computed property on your type to let you use it as a key:

class CellThing {
  // Convenience static computed property to get the wrapped metatype value.
  static var hashable: HashableType<CellThing> { return HashableType(self) }
}

class A : CellThing {}
class B : CellThing {}

var recycle: [HashableType<CellThing>: [CellThing]] = [:]

recycle[A.hashable] = [A(), A(), A()]
recycle[B.hashable] = [B(), B()]

print(recycle[A.hashable]!) // [A, A, A]
print(recycle[B.hashable]!) // [B, B]

这篇关于制作一个 Swift 字典,其中键是“类型"?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆