在Swift中,如何指定一个类型别名(typealias),将RawRepresentable限制为String?

17
我正在尝试定义一个需要实现原始值为字符串的枚举类型的协议。
我不认为目前可以强制使用枚举类型,并且我也不确定我真的很关心,只要在后续过程中我可以调用fromRaw()并接收到字符串即可。
因此,我正在尝试保持以下代码的简洁性,同时限制Beta为一个枚举类型且其原始值为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


只是出于好奇,你有检查过 https://dev59.com/6GAg5IYBdhLWcg3wDHQ3 吗? - Rikkles
我已经这样做了,那里的答案描述了我在示例中展示的内容。 - Paul Young
4个回答

8

我来补充一下,因为这篇文章有点旧了。你的更新示例现在适用于Swift 2+,并且会在编译时发出警告,除非它是一个字符串类型,否则.Zeta是模棱两可的。

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

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

作为一则附注,我认为这就是苹果处理 CodingKey 的方式。 - Ash

3
现在这是可能的。例如,以下协议允许类定义自己的“输入”参数枚举,符合字符串格式:
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
    }

}

这类似于苹果在Codable协议中处理CodingKey的方式。我发现在将任意类类型存储在数据库中时很有用。


0

这并不是很有用,因为虽然枚举不是RawRepresentable,但你不能执行Wat(rawValue: "Steve") - Chilv

0

让我们来看看我们的选择。首先,它(截至Xcode 6 beta 5)是一个众所周知的限制,我们不能以简单和预期的方式指定枚举类型约束。其次,您需要非常清晰的东西:能够调用fromRaw(String)。第三,您想要编译器错误。我会说你最好的选择是编写一个协议来实现这一点,并向消费者推送确保他/她给你一个fromRaw(String)的要求。在这种情况下,这是我会做的事情,简化您的第二个代码片段:

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(),因为它有效。”

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接