【问题标题】:In Swift, how can I specify a typealias that restricts RawRepresentable to String?在 Swift 中,如何指定将 RawRepresentable 限制为 String 的类型别名?
【发布时间】:2014-08-09 19:33:10
【问题描述】:

我正在尝试定义一个需要实现 enum 和原始值 String 的协议。

我不相信目前可以强制使用 enum,而且我不确定我是否真的在乎,只要我可以致电 fromRaw()并收到String

因此,我试图保持以下内容的简洁性,同时将Beta 限制为enum,其中原始值为String

protocol Alpha {
    typealias Beta: RawRepresentable
}

struct Gamma: Alpha {
    enum Beta: String {
        case Delta = "delta"
    }
}

struct Eta<T: Alpha, U: RawRepresentable where T.Beta == U> {
    let alpha: T
    let beta: U
    init(alpha: T, beta: U) {
        self.alpha = alpha
        self.beta = beta
        println("beta is: \(beta.toRaw())")
    }
}

let gamma = Gamma()
Eta(alpha: gamma, beta: .Delta) // "beta is delta"

上面的问题是允许其他原始值,因此这是有效的:

struct Epsilon: Alpha {
    enum Beta: Int {
        case Zeta = 6
    }
}

let epsilon = Epsilon()
Eta(alpha: epsilon, beta: .Zeta) // "beta is 6"

解决我目前正在这样做的问题:

protocol StringRawRepresentable: RawRepresentable {
    class func fromRaw(raw: String) -> Self?
}

protocol Alpha {
    typealias Beta: StringRawRepresentable
}

struct Gamma: Alpha {
    enum Beta: String, StringRawRepresentable {
        case Delta = "delta"
    }
}

// Type 'Epsilon' does not conform to protocol 'Alpha'
struct Epsilon: Alpha {
    enum Beta: Int {
        case Zeta = 6
    }
}

struct Eta<T: Alpha, U: StringRawRepresentable where T.Beta == U> {
    let alpha: T
    let beta: U
    init(alpha: T, beta: U) {
        self.alpha = alpha
        self.beta = beta
        println("beta is: \(beta.toRaw())")
    }
}

let gamma = Gamma()
Eta(alpha: gamma, beta: .Delta) // "beta is delta"

有没有办法可以在原始示例中以不同的方式声明typealias,以将RawRepresentable 限制为String


更新

指定U: RawRepresentable where U.Raw == String 似乎很有希望,所以我试了一下:

protocol Alpha {
    typealias Beta: RawRepresentable
}

struct Gamma: Alpha {
    enum Beta: String {
        case Delta = "delta"
    }
}

struct Eta<T: Alpha, U: RawRepresentable where T.Beta == U, U.Raw == String> {
    let alpha: T
    let beta: U
    init(alpha: T, beta: U) {
        self.alpha = alpha
        self.beta = beta

        // Execution was interrupted, reason: EXC_BAD_ACCESS (code=EXC_I386_GPFLT).
        println("beta is: \(beta.toRaw())")
    }
}

let gamma = Gamma()
Eta(alpha: gamma, beta: .Delta) // "beta is delta"

struct Epsilon: Alpha {
    enum Beta: Int {
        case Zeta = 6
    }
}

let epsilon = Epsilon()
Eta(alpha: epsilon, beta: .Zeta) // Error only occurs when this is executed

虽然这在技术上阻止了使用 String 以外的任何内容,但我正在寻找编译时约束,这似乎会导致运行时异常。

如果可能的话,我也希望由协议强制执行,而不是消费者需要检查.Raw == String

【问题讨论】:

标签: swift rawrepresentable


【解决方案1】:

只是为了补充一点,因为它有点旧,您更新的示例现在可以在 swift 2+ 中运行,并且会在编译时抱怨 .Zeta 是模棱两可的,除非它是 String 类型。

您还可以将检查放入协议扩展的模式匹配中。举个例子:

extension SequenceType where Generator.Element:RawRepresentable,
                         Generator.Element.RawValue == String {
    func toStringArray() -> [String] {
        return self.map { $0.rawValue }
    }
}

【讨论】:

  • 附带说明,我相信这就是 Apple 处理 CodingKey 的方式
【解决方案2】:

现在应该可以了。例如,以下协议允许类定义自己的符合字符串的“输入”参数枚举:

protocol AttributeContainer {
   associatedtype InputKey: RawRepresentable where InputKey.RawValue: StringProtocol
   func set(value: Any?, for inputKey: InputKey)
}

可以这样使用:

class MyClass: AttributeContainer {

    enum AttributeKey: String {
       case attributeA, attributeB, attributeC
    }

    func set(value: Any?, for inputKey: InputKey) {
        // Handle the setting of attributes here
    }

}

这类似于 Apple 在Codable 协议中处理CodingKey 的方式。我发现它在做诸如在数据库中存储任意类类型之类的事情时很有用。

【讨论】:

    【解决方案3】:

    让我们在这里看看我们的选项。首先,它(从 Xcode 6 beta 5 开始)是一个众所周知的限制,我们无法以简单且预期的方式指定枚举类型约束。其次,您需要非常清楚:能够调用fromRaw(String)。第三,你想要一个编译器错误。我想说你最好的选择是编写一个协议来做到这一点,并向消费者下达确保他/她给你fromRaw(String)的要求。在这种情况下,这就是我要做的,简化你的第二个代码 sn-p:

    protocol Alpha {
        typealias Beta: RawRepresentable
        func fromRaw(raw: String) -> Beta?
    }
    
    struct Gamma: Alpha {
        enum Beta: String {
            case Delta = "delta"
            case Omega = "omega"
        }
        func fromRaw(raw: String) -> Beta? {
            return Beta.fromRaw(raw)
        }
    }
    
    struct Eta<T: Alpha, U: RawRepresentable where T.Beta == U> {
        let alpha: T
        let beta: U
        init(alpha: T, beta: U) {
            self.alpha = alpha
            self.beta = beta
            println("beta is: \(beta.toRaw())")
        }
    }
    
    let gamma = Gamma()
    let a = Eta(alpha: gamma, beta: .Delta) // "beta is delta"
    println(gamma.fromRaw("delta"))  // Optional(Enum Value)
    println(gamma.fromRaw("omega")!) // Enum Value
    

    从哲学上讲,这更符合您的需求:您说“我想要的东西不仅是 RawRepresentable,还想要一个 fromRaw(String)。弄清楚你是如何给我的”。 Gamma 结构是最简单的例子,消费者详细说明他的枚举,然后说“好的,我可以给你我的标准fromRaw(),因为它有效。

    【讨论】:

      【解决方案4】:

      我和一位同事讨论了这一点,在 Swift 2.0/2.1 中,您可以使用协议:https://gist.github.com/designatednerd/5645d286df0ce939714b

      在我正在使用的应用中试用过,效果很好。 :)

      【讨论】:

      • 这不是很有用,因为虽然枚举不是 RawRepresentable,但你不能这样做 Wat(rawValue: "Steve")
      猜你喜欢
      • 1970-01-01
      • 2016-07-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-08-10
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多