【问题标题】:CustomStringConvertible in enum枚举中的 CustomStringConvertible
【发布时间】:2017-07-23 04:38:40
【问题描述】:

我在课堂上有以下枚举。

enum Attributes: String, CustomStringConvertible {
    case eventDate
    case eventName
    case eventType
    case country

    var description: String {
        return self.rawValue
    }
}

当我尝试使用以下代码时,编译器会报以下错误。

var attributesList: [String] {
    return [
        Attributes.eventDate, //<-- Compiler error on this row
        Attributes.eventName,
        Attributes.eventType,
        Attributes.country]
}

无法将“属性”类型的值转换为预期的元素类型“字符串”

“CustomStringConvertible”协议不应该返回“描述”吗? 上面的代码有什么问题?

【问题讨论】:

  • 如果有人想对某个问题投反对票,那么该人应该有礼貌地解释原因。

标签: swift3 enums customstringconvertible


【解决方案1】:

TL;DR - 它不起作用,因为 Attributes 的数组不能分配给 Strings 的数组,它们都是不匹配的类型,而 Swift 不这样做类型之间的自动转换,需要指定显式转换。


在 Swift 中,当您使用数组字面量初始化数组时,会发生以下情况:

let words = ["hello", "world"]
  • 编译器识别出一个数组文字被分配给一个名为words 的变量。由于我们没有指定words 的类型,因此隐含地假定了一个数组。数组底层元素的类型是根据数组字面量的内容确定的。
  • 在这种情况下,数组字面量是String 类型的集合;这很容易被编译器理解
  • 由于 LHS 类型是一个数组,RHS 结构是一个数组字面量,并且由于 LHS 类型 (Array) 符合名为 ExpressibleByArrayLiteral 的预定义协议,该协议具有关联的类型约束以匹配 @987654329 @,编译器实际上会将我们的行转换为以下内容

例子:

let words = [String].init(arrayLiteral: ["hello", "world"]) // we do not call this init directly

这就是使用数组字面量进行初始化的方式。在上面的例子中,由于我们没有指定数组的类型,所以隐式类型设置会起作用。如果我们指定了不匹配的类型,则分配将失败,因为 ExpressibleByArrayLiteral 需要关联的数组字面量 Element 类型和您分配给的实际数组才能匹配。

所以以下失败:

let words:[String] = [1, 2] // array literal has Element=Int, array has Element=String

这也说明IntString之间没有隐式类型转换,即使Int符合CustomStringConvertible

在您的情况下,您尝试将由Attributes 组成的数组文字分配给String 的数组。这是类型不匹配。这就是它失败的原因。

如果您声明协议一致性,则以下行将起作用:

var attributesList: [CustomStringConvertible] {
    return [
        Attributes.eventDate,
        Attributes.eventName,
        Attributes.eventType,
        Attributes.country
    ]
}
// note that we have an array of CustomStringConvertible protocol,
// each element here is still of Attributes type
// a type conforming to a protocol can be cast to an instance
// of that protocol automatically
// The above initialisation works simply because the following
// also works without any further action required
// let a:CustomStringConvertible = Attributes.country

如果你真的想要一个字符串值列表,你需要将它显式映射到一个字符串:

var attributesList1: [String] {
    return [
        Attributes.eventDate,
        Attributes.eventName,
        Attributes.eventType,
        Attributes.country
        ].map { $0.description }
}

var attributesList2: [String] {
    return [
        Attributes.eventDate.description,
        Attributes.eventName.description,
        Attributes.eventType.description,
        Attributes.country.description
        ]
}

【讨论】:

  • 或者你可以在最后一个例子中使用rawValue而不是description
猜你喜欢
  • 2017-07-26
  • 1970-01-01
  • 2011-03-22
  • 2015-08-08
  • 2010-11-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多