【问题标题】:Can you check if a Type (not an instance) is a subclass of another Type?你能检查一个类型(不是实例)是否是另一个类型的子类吗?
【发布时间】:2020-01-21 20:55:49
【问题描述】:

鉴于此代码...

class Vehicle{}

class Car : Vehicle {}

class Honda : Car {}

你会如何写下面的函数'findFirst'...

class TypeManager {

    var managedTypes:[Any.Type]?

    func findFirst(_ type:Any.Type) -> Any.Type? {
        return managedTypes.first{ t in t is type.Type } // <-- Doesn't like 'type'
    }
}

var typeManager = TypeManager()
typeManager.managedTypes = [
    String.self,
    Int.self,
    Honda.self
]

let firstCarType = typeManager.findFirst(Car.Type)

注意:这实际上是对字典进行反向键查找。在一个完美的解决方案中,我首先尝试在“类型”上找到一个完全匹配的,如果没有找到,就选择一个“类型”的子类。我只是简化了(伪造的)代码以专注于匹配部分。

【问题讨论】:

  • 你能举一个例子吗?
  • 我正在搜索类型列表(不是实例),以寻找任何符合直接匹配要求或给定类型的子类的要求。这在 C# 中使用 CanAssignFrom 是微不足道的。
  • first(where: { $0 is Foo.Type }) 呢?
  • 我以为is 只检查实例。
  • 类也是(元类型的)实例。

标签: swift types casting


【解决方案1】:

要扩展 Martin R's great answer,您可以进行如下的数组扩展:

extension Array {
    func first<T>(ofType: T.Type) -> T.Type?  {
        return first { $0 is T.Type } as? T.Type
    }
    func first<T>(ofExactType type: T.Type) -> T.Type? {
        return first { $0 as? Any.Type == type } as? T.Type
    }
}

class Vehicle {}
class Car : Vehicle {}
class Honda: Car {}

let carTypes = [Honda.self, Vehicle.self, Car.self] // Inferred type [Vehicle]
print(carTypes.first(ofType: Car.self) ?? "n/a") // prints Honda
print(carTypes.first(ofExactType: Car.self) ?? "n/a") // prints Car

另外,仅供参考,$0 as? Any.Type == type$0 as? Any.Type == T.self 相同。任何一个都可以。

【讨论】:

  • 完美!从技术上讲,@Martin 首先得到了它,但他把它放在了 cmets 中,所以我不能接受!大声笑!
  • 他确实做到了;很好的问题,也是
  • 该死!得把这个拉回来。 Lemme 用更多遗漏了重要部分的内容更新了我的问题。对不起! ://
  • 我现在正在努力将类型传递给函数,而不是像这里那样进行硬编码。有什么想法吗?
  • 投了赞成票!惊人的!!!当您想要完全匹配时,您确实使用传入的值。如果你想要一个子类,你可以使用推断的 T。太棒了!我喜欢这样的日子!实际上,我会把答案转回给你。他有444k。他不会错过10分!哈哈!
【解决方案2】:

类是元类型的实例,可以通过isas? 进行检查。 您可以使用泛型函数来传递所寻求的类型:

class TypeManager {

    var managedTypes:[Any.Type] = []

    func findFirst<T>(_: T.Type) -> Any.Type? {
        return managedTypes.first { $0 is T.Type }
    }
}

例子:

if let firstCarType = typeManager.findFirst(Car.self) {
    print(firstCarType) // Honda
}

或者使用条件绑定和compactMap:

class TypeManager {

    var managedTypes:[Any.Type] = []

    func findFirst<T>(_: T.Type) -> T.Type? {
        return managedTypes.compactMap { $0 as? T.Type }.first
    }
}

这样做的好处是返回的类型是T.Type? 而不是Any.Type?。 (如果列表很大并且需要短路,请使用managedTypes.lazy.compactMap。)

【讨论】:

  • 啊啊啊!一个通用的!伙计,甚至没有想到这一点,但就是这样,完美! (Prolly 为什么你在 444k !!!哈哈)
  • 另外,如果我错了,请纠正我,但您实际上并没有在这里使用“类型”。你只是用它来输入通用的“T”,然后它就被忽略了,对吗?
  • @MarkA.Donohoe:你是对的(因此实际上不需要内部名称type)。编译器从上下文中推断出T(例如Car)并将其传递给函数。
  • 只是出于好奇,如果我想在 T 上进行 exact 匹配,而不仅仅是一个子类怎么办? '==' 和 '===' 都试过了,但都没有奏效。我问的原因是这实际上是字典中的反向键查找,其中一个键可能用于值“汽车”,但另一个键用于值“本田”。如果我想要“汽车”而不是“本田”的钥匙,我需要完全匹配。
猜你喜欢
  • 2010-10-21
  • 1970-01-01
  • 2017-07-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-02-13
  • 2011-01-19
  • 1970-01-01
相关资源
最近更新 更多