为什么 [SomeStruct] 不能转换为 [Any]? [英] Why isn't [SomeStruct] convertible to [Any]?

查看:35
本文介绍了为什么 [SomeStruct] 不能转换为 [Any]?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

考虑以下事项:

struct SomeStruct {}

var foo: Any!
let bar: SomeStruct = SomeStruct()

foo = bar // Compiles as expected

var fooArray: [Any] = []
let barArray: [SomeStruct] = []

fooArray = barArray // Does not compile; Cannot assign value of type '[SomeStruct]' to type '[Any]'

我一直试图找到这背后的逻辑,但没有运气.值得一提的是,如果将结构体更改为类,则效果很好.

I've been trying to find the logic behind this, but with no luck. It's worth mentioning if you change the struct to a class, it works perfectly.

人们总是可以添加一种解决方法并映射 fooArray 的每个对象并将它们强制转换为 Any 类型,但这不是这里的问题.我正在寻找有关为什么会出现这种情况的解释.

One could always add a workaround and map each object of the fooArray and cast them to type of Any, but that is not the issue here. I'm looking for an explanation on why this is behaving like it is.

有人可以解释一下吗?

这个问题让我想到了这个问题.>

推荐答案

Swift 3 更新

从 Swift 3(特别是 Xcode 8 beta 6 附带的构建)开始,集合类型现在可以在后台执行从值类型元素集合到抽象类型元素集合的转换.

Swift 3 Update

As of Swift 3 (specifically the build that ships with Xcode 8 beta 6), collection types can now perform under the hood conversions from value-typed element collections to abstract-typed element collections.

这意味着现在将编译以下内容:

This means that the following will now compile:

protocol SomeProtocol {}
struct Foo : SomeProtocol {}

let arrayOfFoo : [Foo] = []

let arrayOfSomeProtocol : [SomeProtocol] = arrayOfFoo
let arrayOfAny : [Any] = arrayOfFoo

<小时>

Swift 3 之前

这一切都始于这样一个事实,即 Swift 中的泛型是不变的——而不是协变的.记住 [Type] 只是 Array 的语法糖,你可以抽象掉数组和 Any 以希望更好地看到问题.


Pre Swift 3

This all starts with the fact that generics in Swift are invariant – not covariant. Remembering that [Type] is just syntactic sugar for Array<Type>, you can abstract away the arrays and Any to hopefully see the problem better.

protocol Foo {}
struct Bar : Foo {}

struct Container<T> {}

var f = Container<Foo>()
var b = Container<Bar>()

f = b // error: cannot assign value of type 'Container<Bar>' to type 'Container<Foo>'

与类类似:

class Foo {}
class Bar : Foo {}

class Container<T> {}

var f = Container<Foo>()
var b = Container<Bar>()

f = b // error: cannot assign value of type 'Container<Bar>' to type 'Container<Foo>'

这种协变行为(向上转换)在 Swift 中的泛型根本不可能.在您的示例中,由于不变性,Array 被视为与 Array 完全无关的类型.

This kind of covariant behaviour (upcasting) simply isn't possible with generics in Swift. In your example, Array<SomeStruct> is seen as a completely unrelated type to Array<Any> due to the invariance.

然而,数组有一个例外——它们可以在幕后默默地处理从子类类型到超类类型的转换.但是,在将具有值类型元素的数组转换为具有抽象类型元素(例如 [Any])的数组时,它们的作用不同.

However, arrays have an exception to this rule – they can silently deal with conversions from subclass types to superclass types under the hood. However, they don't do the same when converting an array with value-typed elements to an array with abstract-typed elements (such as [Any]).

要解决这个问题,您必须执行自己的逐个元素转换(因为单个元素是协变的).实现这一点的常用方法是使用 map(_:):

To deal with this, you have to perform your own element-by-element conversion (as individual elements are covariant). A common way of achieving this is through using map(_:):

var fooArray : [Any] = []
let barArray : [SomeStruct] = []

// the 'as Any' isn't technically necessary as Swift can infer it,
// but it shows what's happening here
fooArray = barArray.map {$0 as Any} 

在这里阻止隐式幕后"转换的一个很好的理由是 Swift 在内存中存储抽象类型的方式.使用存在容器"是为了在固定内存块中存储任意大小的值——这意味着对于无法容纳在该容器中的值可能会发生昂贵的堆分配(只允许对内存的引用存储在取而代之的是这个容器).

A good reason to prevent an implicit 'under the hood' conversion here is due to the way in which Swift stores abstract types in memory. An 'Existential Container' is used in order to store values of an arbitrary size in a fixed block of memory – meaning that expensive heap allocation can occur for values that cannot fit within this container (allowing just a reference to the memory to be stored in this container instead).

因此,由于数组现在在内存中的存储方式发生了重大变化,因此禁止隐式转换是非常合理的.这让程序员清楚地知道他们必须强制转换数组的每个元素——导致内存结构的这种(可能代价高昂的)变化.

Therefore because of this significant change in how the array is now stored in memory, it's quite reasonable to disallow an implicit conversion. This makes it explicit to the programmer that they're having to cast each element of the array – causing this (potentially expensive) change in memory structure.

有关 Swift 如何处理抽象类型的更多技术细节,请参阅 这个奇妙的 WWDC谈论这个话题.如需进一步了解 Swift 中的类型变化,请参阅这篇精彩博文主题.

For more technical details about how Swift works with abstract types, see this fantastic WWDC talk on the subject. For further reading about type variance in Swift, see this great blog post on the subject.

最后,请务必查看@dfri下面的评论数组可以隐式转换元素类型的另一种情况——即当元素可以桥接到 Objective-C 时,它们可以由数组隐式完成.

Finally, make sure to see @dfri's comments below about the other situation where arrays can implicitly convert element types – namely when the elements are bridgeable to Objective-C, they can be done so implicitly by the array.

这篇关于为什么 [SomeStruct] 不能转换为 [Any]?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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