当前位置: 首页 > 面试题库 >

检查Swift对象是否是给定元类型的实例

邰棋
2023-03-14
问题内容

我需要保留Swift元类型的集合并编写一个函数,该函数将检查给定对象是否是其中之一的实例。我可以在Java中轻松做到这一点:

Class c = x.getClass();
c.isInstance(someObj)

但是,我不知道如何在Swift中做到这一点:

var isInt = 7 is Int.Type // compiles

let x = Int.self
var isInt = 7 is x // compiler error - Use of undeclared type 'x'

这甚至可以在Swift中完成吗?


问题答案:

不幸的是,您目前只能对is运算符使用命名类型,还不能对其使用任意的元类型值(尽管实际上 应该 可以使用IMO )。

假设您可以控制要与之进行比较的元类型的创建,则达到相同结果的一种解决方案是创建一个包装器类型,该包装器类型具有一个初始化器,该初始化器存储一个is对通用占位符执行检查的闭包:

struct AnyType {

  let base: Any.Type
  private let _canCast: (Any) -> Bool

  /// Creates a new AnyType wrapper from a given metatype.
  /// The passed metatype's value **must** match its static value,
  /// i.e `T.self == base`.
  init<T>(_ base: T.Type) {
    precondition(T.self == base, """
      The static value \(T.self) and dynamic value \(base) of the passed \
      metatype do not match
      """)

    self.base = T.self
    self._canCast = { $0 is T }
  }

  func canCast<T>(_ x: T) -> Bool {
    return _canCast(x)
  }
}
protocol P {}
class C : P {}
class D : C {}

let types = [
  AnyType(P.self), AnyType(C.self), AnyType(D.self), AnyType(String.self)
]

for type in types {
  print("C instance can be typed as \(type.base): \(type.canCast(C()))")
  print("D instance can be typed as \(type.base): \(type.canCast(D()))")
}

// C instance can be typed as P: true
// D instance can be typed as P: true
// C instance can be typed as C: true
// D instance can be typed as C: true
// C instance can be typed as D: false
// D instance can be typed as D: true
// C instance can be typed as String: false
// D instance can be typed as String: false

这种方法的唯一局限性在于,鉴于我们正在执行is检查T.self,因此必须强制执行T.self == base。例如,我们不能接受AnyType(D.self as C.Type),因为那么T.selfC.self同时baseD.self

但是,在您的情况下这应该不成问题,因为我们只是AnyType从编译时已知的元类型构造而来。

但是,如果您无法控制元类型的创建(即您从API获得元类型),那么您在使用元数据时所受的限制就更大了。

如@adev所说,您可以使用type(of:)来获取给定实例的动态元类型,并使用==运算符确定两个元类型是否等效。但是,这种方法的一个问题是它同时忽略了类的层次结构和协议,因为子类型的元类型不会与父类型的元类型进行比较。

关于类的一种解决方案是使用Mirror,如本问答中所示:

/// Returns `true` iff the given value can be typed as the given
/// **concrete** metatype value, `false` otherwise.
func canCast(_ x: Any, toConcreteType destType: Any.Type) -> Bool {
  return sequence(
    first: Mirror(reflecting: x), next: { $0.superclassMirror }
  )
  .contains { $0.subjectType == destType }
}

class C {}
class D : C {}

print(canCast(D(), toConcreteType: C.self)) // true
print(canCast(C(), toConcreteType: C.self)) // true
print(canCast(C(), toConcreteType: D.self)) // false
print(canCast(7, toConcreteType: Int.self)) // true
print(canCast(7, toConcreteType: String.self)) // false

我们正在使用sequence(first:next:)从动态类型x到它可能具有的任何超类元类型来创建元类型序列。

但是,此方法仍不适用于协议。希望该语言的未来版本将提供更丰富的反射API,使您能够比较两个元类型值之间的关系。

然而,考虑到能够使用上面的知识Mirror,我们可以用它来解除上述限制T.self == base从我们的AnyType包装由单独处理类元类型:

struct AnyType {

  let base: Any.Type
  private let _canCast: (Any) -> Bool

  /// Creates a new AnyType wrapper from a given metatype.
  init<T>(_ base: T.Type) {

    self.base = base

    // handle class metatypes separately in order to allow T.self != base.
    if base is AnyClass {
      self._canCast = { x in
        sequence(
          first: Mirror(reflecting: x), next: { $0.superclassMirror }
        )
        .contains { $0.subjectType == base }
      }
    } else {
      // sanity check – this should never be triggered,
      // as we handle the case where base is a class metatype.
      precondition(T.self == base, """
        The static value \(T.self) and dynamic value \(base) of the passed \
        metatype do not match
        """)

      self._canCast = { $0 is T }
    }
  }

  func canCast<T>(_ x: T) -> Bool {
    return _canCast(x)
  }
}

print(AnyType(D.self as C.Type).canCast(D())) // true

其中的情况下T.self是一个类元类型应该是其中唯一的情况下T.self != base,与协议,当T是一些协议PT.TypeP.Protocol,它是协议本身的类型。目前,此类型只能保存valueP.self



 类似资料:
  • 我有一个数组,它由组成。我想对它进行迭代,并找到所有作为数组实例的元素。 如何在Swift中检查对象是否属于给定类型?

  • 问题内容: 我有一个由组成的数组。我想遍历它,并找到所有属于数组实例的元素。 如何在Swift中检查对象是否为给定类型? 问题答案: 如果要检查特定类型,可以执行以下操作: 您可以使用“ as!” 如果类型不正确,则会引发运行时错误 您也可以一次检查一个元素:

  • 对于这个示例: null 是否有类似这样的语句用于此检查?或者我应该使用

  • web3.utils.isBN()方法用来检查给定的参数是否是一个BN.js实例对象。 调用: web3.utils.isBN(bn) 参数: bn - Object: 要检查的对象 返回值 Boolean:如果参数为BN对象则返回true,否则返回false 实例代码: var number = new BN(10); web3.utils.isBN(number); > true

  • 问题内容: 通过使用Java反射,我们可以轻松知道对象是否为数组。判断对象是否为集合(Set,List,Map,Vector …)的最简单方法是什么? 问题答案:

  • 使用web3.utils.isBigNumber()方法检查给定的参数是否是一个 BigNumber.js对象表示的大数。 调用: web3.utils.isBigNumber(bignumber) 参数: bignumber - Object: 要检查的对象 返回值: Boolean:如果参数是BigNumber.js对象则返回true,否则返回false 实例代码: var number =