为什么向下转换 Swift 中的数组项?

Why downcast array item in Swift?

如果数组项的子 class 已知,为什么我必须向下转换 Swift 中的数组项?

 > class B {let t: String = "-B-"}
 > class B1:B {let t1: String = "-B1-"}
 > class B2:B {let t2: String = "-B2-"}
 > let bunch = [B(), B1(), B2()]

符合预期:

 > print(type(of:bunch))
 Array<B>

已知数组每个元素的子class:

 > for item in bunch {print(type(of:item))}
 B
 B1
 B2

然而(如文档所述)我们无法访问项目的子 class 成员,我们必须向下 -class:

> if bunch[1] is B1 {let b1 = bunch[1] as! B1; print(b1.t1)} 
-B1-

因为这行不通:

> bunch[1].t1
error: repl.swift:17:6: error: value of type 'B' has no member 't1'; did you mean 't'?

为什么 Swift 使用 type(of:) 可以确定 sub-class 但在访问该 sub-class 中的成员时不能推断它?是错误、历史遗留问题,还是我遗漏了什么?

简单的答案是,一旦声明了 B 类型的数组,编译器就会将数组中的所有元素都视为 B 类型,并且不会根据任何类型自动推断方法subclass 存在。您似乎想知道为什么 compiler/array 不够智能,无法确定 let 声明数组的 class,而理论上它可以。我不确定许多语言是否支持这样的功能,无论如何你可以像你一样简单地打开它来做同样的事情,因为在这种情况下你知道它。

Swift 数组是 单一类型项的集合。 数组的类型

let bunch = [B(), B1(), B2()]

被推断为 [B] 因为 B 是给定三个元素的“最近公共超类”。因此,bunch[1] 的类型为 B 而不是 B1。这就是 bunch[1].t1 无法编译的原因。

当然,您可以在运行时使用 type(of:) 打印数组元素的实际类型,或者对照 B1 进行检查。后者最好使用可选绑定来完成:

if let b1 = bunch[1] as? B1 {
    print(b1.t1)
}