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

为什么[SomeStruct]无法转换为[Any]?

赖诚
2023-03-14
问题内容

考虑以下:

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]'

我一直在寻找背后的逻辑,但是没有运气。值得一提的是,如果将结构更改为类,则效果很好。

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

有人可以解释一下吗?


问题答案:

Swift 3更新

从Swift 3(特别是Xcode 8 beta 6附带的内部版本)开始,集合类型现在可以在幕后执行,从值类型元素集合到抽象类型元素集合的转换。

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

protocol SomeProtocol {}
struct Foo : SomeProtocol {}

let arrayOfFoo : [Foo] = []

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

迅捷前3

这一切都始于Swift中的泛型是不变的,而不是协变的。记住,这[Type]只是的语法糖Array<Type>,您可以抽象出数组并Any希望更好地看到问题。

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<SomeStruct>被视为完全不相关的类型Array<Any>

但是,数组是该规则的例外–它们可以在后台默默地处理从子类类型到超类类型的转换。但是,在将具有值类型元素的数组转换为具有抽象类型元素(例如[Any])的数组时,它们的作用不同。

为了解决这个问题,您必须执行自己的逐个元素转换(因为各个元素是协变的)。实现此目的的常用方法是使用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将抽象类型存储在内存中的方式。使用“现有容器”是为了将任意大小的值存储在固定的内存块中–这意味着可能会为无法容纳在该容器中的值进行昂贵的堆分配(仅允许引用要存储的内存)该容器)。

因此,由于数组在内存中的存储方式发生了重大变化,因此禁止隐式转换是非常合理的。这使程序员可以清楚地知道,他们必须转换数组的每个元素-
导致内存结构发生这种更改(可能非常昂贵)。

有关Swift如何与抽象类型一起工作的更多技术细节,请参阅关于主题的WWDC精彩演讲。有关Swift中类型差异的更多信息,请参见有关该主题的精彩博客文章。

最后,确保在下面看到@dfri关于数组可以隐式转换元素类型的其他情况的注释–即,当元素可桥接到Objective-C时,它们可以由数组隐式完成。



 类似资料:
  • 问题内容: 我有一个子过程命令,输出一些字符,例如’\ xf1’。我正在尝试将其解码为utf8,但出现错误。 上面抛出: 当我使用’latin-1’时它可以工作,但是utf8也不能工作吗?我的理解是latin1是utf8的子集。 我在这里想念什么吗? 编辑: 问题答案: 您已经将Unicode与UTF-8混淆了。Latin-1是Unicode的子集,但不是UTF-8的子集。 避免像瘟疫一样思考各个

  • 问题内容: 我想知道为什么你不能做: 我发现这将需要运行时在片上执行循环以转换每个元素,这将是非惯用的Go。这很有道理。 然而,这可能不会被刚刚走样编译器解决的,所以在内部它们是相同的,他们使用相同类型的头底下?我猜答案虽然不是我好奇为什么。 问题答案: 这个: 是类型转换。根据规范,转换具有特定的规则: 在以下任何一种情况下,可以将非恒定值转换为类型: 是分配给。 的类型,并且具有相同的基础类型

  • 问题内容: 我经常使用此代码将视图居中: 它在Firefox,Internet Explorer和Chrome上运行良好,但在Safari上却无法运行。 在Safari Web浏览器中将图像居中的解决方法是什么? 问题答案: 您需要另一个供应商前缀样式。

  • 问题内容: 我可以隐式地将int转换为long,并将long转换为Long。为什么无法将int隐式转换为Long?为什么Java不能在示例的最后一行进行隐式转换? 问题答案: 和是对象。装箱/拆箱仅适用于原语。做的就像,那不是不!另外,请记住,之间没有继承,所以甚至无效

  • 问题内容: 为什么写: 问题答案: 实现的对象是。 您要在其中覆盖的方法应将对象作为参数,而将其强制转换为。您的实现应描述如何与另一个进行比较。 要真正进行排序,您可能还需要制作工具,然后将实际逻辑复制粘贴到其中。

  • 问题内容: 我当前正在使用条件来检索用户的详细信息,但是当尝试用合适的用户查询详细信息对象时,我得到了ClassCastException。 我的标准代码; 我也尝试使用; 两者都给我ClassCastException。我知道我可以通过让用户实现Serializable来轻松解决它,但是还有其他解决方案吗? 问题答案: 唯一的其他解决方案是实现Externalizable。