为啥 Swift 在将多个类型项放入 Array 时不会对 Any 进行类型推断

Posted

技术标签:

【中文标题】为啥 Swift 在将多个类型项放入 Array 时不会对 Any 进行类型推断【英文标题】:Why Swift doesn't type inference to Any when put multiple type item in Array为什么 Swift 在将多个类型项放入 Array 时不会对 Any 进行类型推断 【发布时间】:2016-08-29 16:15:03 【问题描述】:

使用Xcode 7.1开发swift 2.2时有两种情况让我很困惑,请看下面的例子,谢谢

首先,在导入 Foundation 时,我声明了一个 testArray,其中包含两个项目,一个 Integer 类型 1 和一个 String 类型“hello”,我的问题是为什么 Swift 类型推断 testArray 为 Array(NSObject) 而不是 Array(Any)

import Foundation
let testArray = [1, "hello"] 
print(testArray.dynamicType) //testArray is Array<NSObject>

其次,当我删除 import Foundation 时,下面的代码无法编译,错误消息是“表达式类型不明确,没有更多内容”,我的问题是为什么 Swift 不在这个中对 Array(Any) 进行类型推断情况,谢谢帮助

let testArray2 = [2, "world"]
print(testArray2) 
//can't compile, error message = "Type of expression is ambiguous without more content"

【问题讨论】:

【参考方案1】:
/// The protocol to which all types implicitly conform.
public typealias Any = protocol<>

Any 只是一个所有类型都隐式遵循的协议——它本身并不是一个具体的类型。 Swift 无法推断出非具体类型的数组,这就是为什么它无法推断出Any,但会成功推断出NSObjectInt 可以桥接到NSNumberString 可以桥接到NSString – 它们都继承自NSObject,这是一个具体类型)。

例如,考虑一下:

protocol Foo 
struct Bar:Foo 
struct Baz:Foo 

let arr = [Bar(), Baz()] // error: Type of expression is ambiguous without more context

因为Foo 是非具体类型,Swift 无法推断出它的数组。你必须明确告诉编译器你希望它的类型是什么:

let arr:[Foo] = [Bar(), Baz()]

AnyObject 也将获得相同的行为(因为它是所有 隐式遵守的协议——但仍不是具体类型):

class Qux 
class Fox 

let a = [Qux(), Fox()] // error: Type of expression is ambiguous without more context

let a1:[AnyObject] = [Qux(), Fox()] // no error

为什么 Swift 无法推断出非具体类型的数组很可能是由于该语言中非具体类型的现有限制——目前大多数非平凡类型都需要具体类型操作。 See this great Q&A for an example.

但老实说,您真的应该更多地考虑您是否实际上需要Any 的数组。我想不出拥有Any 数组的单一实际应用,因为所有内容都隐含地符合元素,因此必须保证它们什么都不做(你不能对可能是任何东西的东西调用特定方法)。当然你可以进行类型转换,但是恢复你一开始就抛弃的类型安全有什么意义呢?

您应该始终尽可能地针对特定类型。您可以为您的值构建一个包装器——这可以是一个简单的struct 来包装几个属性,或者是一个type erasure 以便将非具体类型包装在一个伪具体类型中。至少,您应该考虑创建自己的数组元素符合的协议。

【讨论】:

最后两段在这里提出了最重要的观点。老实说,NSObject(或 AnyObject 在这里不起作用,但我看到它一直在使用)并不比 Any 更具体。 @originaluser2 你的解释真的很有帮助,非常感谢 @c41ux 乐于助人:)【参考方案2】:

因为它不会自动识别Any的数组

如果你定义它会起作用

let testArray2 :[Any] = [2, "world"]

Foundation 库导入NS API,它会自动将2 转换为NSNumber"world"NSString,并自动将其转换为NSObject 的数组

【讨论】:

1.回答:有趣,他问为什么,你只说因为是这样。 2. 答:如果你使用import Foundation2就是Int"world"就是String。仅仅因为没有可以找到的原生 Swift 类型(谁知道它为什么不识别 [Any])它需要 [NSObject] @iGodric,嘿,你错了。1。看看他的错误:Type of expression is ambiguous without more content,它无法自动识别它,因为编译器就是这样工作的。 2. NSObject 的数组必须包含 NSObjects 。如果您将执行import Foundation 并执行下一个代码print(testArray[0].dynamicType) print(testArray[1].dynamicType),则输出将是__NSCFNumber,这是NSNumber(而不是Int)和_NSContiguousString,这是NSString而不是String。感谢Funny 的评论。 To 1. 是的,但是说“这就是编译器的工作方式”的解释是没有解释的。至 2. 你所说的是正确的,但你在我的评论中做出假设,我没有说。是的,在导入 Foundation 并创建具有多种类型的数组时,2NSNumber"world"NSString,但这只是因为推断的类型是 [AnyObject]。但是导入Foundation 本身并不会自动将2 转换为NSNumber,这就是我想说的。您的回答可能具有误导性。我的第一条评论可能听起来很刺耳。

以上是关于为啥 Swift 在将多个类型项放入 Array 时不会对 Any 进行类型推断的主要内容,如果未能解决你的问题,请参考以下文章

Swift学习笔记之---Array数组

Swift学习笔记之---Array数组

无法将“Swift.Array<Any>”类型的值转换为“Swift.Dictionary<Swift.String, Any>”

学习Swift -- 数组(Array) - 持续更新

Python Array,试图将文本文件放入数组中

从不同类型的多个用户输入创建一个数组