在通用函数中从枚举中获取 rawValue
Get rawValue from enum in a generic function
2015 年 8 月 28 日更新:
这将在 Swift 2
中解决
见Twitter response from Swift compiler developer
2015 年 10 月 23 日更新:
使用 Swift 2 个泛型,您仍然无法获得 rawValue。你这样做可以获得关联值。
原题:
我有一些generic reflection code写在swift。在该代码中,我无法获取基于枚举的属性的值。问题归结为我无法对 Any
类型的 属性 值执行 .rawValue
。 Swift 反射代码将 return 枚举的值作为 Any
类型。那么我怎样才能从 Any 到 AnyObject 这是枚举的原始值。
到目前为止我发现的唯一解决方法是使用协议扩展所有枚举。您可以在下面看到使用此解决方法的单元测试。
有没有办法在不向原始枚举添加代码的情况下解决这个问题?
对于我的反射代码,我需要 getRawValue
方法签名保持原样。
class WorkaroundsTests: XCTestCase {
func testEnumToRaw() {
let test1 = getRawValue(MyEnumOne.OK)
XCTAssertTrue(test1 == "OK", "Could nog get the rawvalue using a generic function")
let test2 = getRawValue(MyEnumTwo.OK)
XCTAssertTrue(test2 == "1", "Could nog get the rawvalue using a generic function")
let test3 = getRawValue(MyEnumThree.OK)
XCTAssertTrue(test3 == "1", "Could nog get the rawvalue using a generic function")
}
enum MyEnumOne: String, EVRawString {
case NotOK = "NotOK"
case OK = "OK"
}
enum MyEnumTwo: Int, EVRawInt {
case NotOK = 0
case OK = 1
}
enum MyEnumThree: Int64, EVRaw {
case NotOK = 0
case OK = 1
var anyRawValue: AnyObject { get { return String(self.rawValue) }}
}
func getRawValue(theEnum: Any) -> String {
// What can we get using reflection:
let mirror = reflect(theEnum)
if mirror.disposition == .Aggregate {
print("Disposition is .Aggregate\n")
// OK, and now?
// Thees do not complile:
//return enumRawValue(rawValue: theEnum)
//return enumRawValue2(theEnum )
if let value = theEnum as? EVRawString {
return value.rawValue
}
if let value = theEnum as? EVRawInt {
return String(value.rawValue)
}
}
var valueType:Any.Type = mirror.valueType
print("valueType = \(valueType)\n")
// No help from these:
//var value = mirror.value --> is just theEnum itself
//var objectIdentifier = mirror.objectIdentifier --> nil
//var count = mirror.count --> 0
//var summary:String = mirror.summary --> "(Enum Value)"
//var quickLookObject = mirror.quickLookObject --> nil
let toString:String = "\(theEnum)"
print("\(toString)\n")
return toString
}
func enumRawValue<E: RawRepresentable>(rawValue: E.RawValue) -> String {
let value = E(rawValue: rawValue)?.rawValue
return "\(value)"
}
func enumRawValue2<T:RawRepresentable>(rawValue: T) -> String {
return "\(rawValue.rawValue)"
}
}
public protocol EVRawInt {
var rawValue: Int { get }
}
public protocol EVRawString {
var rawValue: String { get }
}
public protocol EVRaw {
var anyRawValue: AnyObject { get }
}
不幸的是,目前这在 Swift 中看起来不太可能,但我已经考虑了您的问题一段时间,我将提出 3 种方法 Swift 团队可以帮助您解决这个问题。
修复枚举的镜像。最直接的解决方案是我相信您已经尝试过的解决方案。您正在尝试构建一个反射库,并且您想要反射一个 Any
值以查看它是否是一个枚举,如果是,您想要查看它是否具有原始值。 rawValue
属性 应该 可以通过此代码访问:
let mirror = reflect(theEnum) // theEnum is of Any type
for i in 0..<mirror.count {
if mirror[i].0 == "rawValue" {
switch mirror[i].1.value {
case let s as String:
return s
case let csc as CustomStringConvertible:
return csc.description
default:
return nil
}
}
}
但是,这不起作用。你会发现镜子的 count
为 0
。我真的认为这是 Swift 团队在实施 Swift._EnumMirror
时的疏忽,我将就此提交雷达。 rawValue
绝对是合法的属性。这是一个奇怪的场景,因为不允许枚举具有其他存储属性。此外,您的枚举声明从未明确符合 RawRepresentable
,也未声明 [=20=] 属性。编译器只是在您键入 enum MyEnum: String
或 : Int
或其他任何内容时推断。在我的测试中,似乎 属性 是在协议中定义还是关联类型的实例并不重要,它仍然应该是镜像中表示的 属性。
允许具有已定义关联类型的协议类型。 正如我在上面的评论中提到的,这是 Swift 中的一个限制,您不能强制转换到具有关联类型要求的协议类型。您不能简单地转换为 RawRepresentable
,因为 Swift 不知道 rawValue
属性 将 return 是什么类型。 RawRepresentable<where RawValue == String>
之类的语法是可以想象的,或者 protocol<RawRepresentable where RawValue == String>
。如果可能的话,您可以尝试通过 switch 语句转换为类型,如:
switch theEnum {
case let rawEnum as protocol<RawRepresentable where RawValue == String>:
return rawEnum.rawValue
// And so on
}
但这在 Swift 中没有定义。如果您只是尝试转换为 RawRepresentable
,Swift 编译器会告诉您只能在泛型函数中使用它,但当我查看您的代码时,这只会让您失望 -洞。通用函数 需要 在编译时输入信息才能工作,而这正是你在 Any
实例中所没有的。
Swift 团队可以将协议更改为更像通用 类 和结构。例如 MyGenericStruct<MyType>
和 MyGenericClass<MyType>
是合法的专用具体类型,您可以对其进行运行时检查并转换为。但是,Swift 团队可能有充分的理由不想使用协议来执行此操作。协议的特殊版本(即具有已知关联类型的协议引用)仍然不是具体类型。我不会为这种能力屏住呼吸。我认为这是我提出的解决方案中最薄弱的一个。
扩展协议以符合协议。 我真的以为我可以让这个解决方案为你工作,但遗憾的是没有。由于 Swift 的内置枚举镜像没有在 rawValue
上提供,我想为什么不实现我自己的通用镜像:
struct RawRepresentableMirror<T: RawRepresentable>: MirrorType {
private let realValue: T
init(_ value: T) {
realValue = value
}
var value: Any { return realValue }
var valueType: Any.Type { return T.self }
var objectIdentifier: ObjectIdentifier? { return nil }
var disposition: MirrorDisposition { return .Enum }
var count: Int { return 1 }
subscript(index: Int) -> (String, MirrorType) {
switch index {
case 0:
return ("rawValue", reflect(realValue.rawValue))
default:
fatalError("Index out of range")
}
}
var summary: String {
return "Raw Representable Enum: \(realValue)"
}
var quickLookObject: QuickLookObject? {
return QuickLookObject.Text(summary)
}
}
太棒了!现在我们所要做的就是将 RawRepresentable
扩展为 Reflectable
这样我们就可以首先转换 theEnum as Reflectable
(不需要关联类型)然后调用 reflect(theEnum)
给我们我们的 awesome自定义镜像:
extension RawRepresentable: Reflectable {
func getMirror() -> MirrorType {
return RawRepresentableMirror(self)
}
}
Compiler error: Extension of protocol 'RawRepresentable' cannot have
an inheritance clause
哇哦?!我们可以扩展具体类型来实现新的协议,例如:
extension MyClass: MyProtocol {
// Conform to new protocol
}
从Swift2开始,我们可以扩展协议来给出功能的具体实现,例如:
extension MyProtocol {
// Default implementations for MyProtocol
}
我认为我们肯定可以扩展协议来实现其他协议,但显然不行!我看不出我们不能让 Swift 这样做的原因。我认为这非常符合 WWDC 2015 上讨论的面向协议的编程范例。实现原始协议的具体类型将免费获得新的协议一致性,但具体类型也可以自由定义自己的新协议方法的版本。我肯定会为此提交增强请求,因为我认为它可能是一个强大的功能。事实上,我认为它在您的反射库中可能非常有用。
编辑: 回想我对答案 2 的不满,我认为广泛使用这些协议有更优雅和现实的可能性,并且实际上结合了我对答案的想法3 关于扩展协议以符合新协议。这个想法是让相关类型的协议符合新协议,这些新协议可以检索原始类型擦除的属性。这是一个例子:
protocol AnyRawRepresentable {
var anyRawValue: Any { get }
}
extension RawRepresentable: AnyRawRepresentable {
var anyRawValue: Any {
return rawValue
}
}
以这种方式扩展协议本身并不是扩展继承。相反,它只是告诉编译器 "Wherever there is a type that conforms to RawRepresentable
, make that type also conform to AnyRawRepresentable
with this default implementation." AnyRawRepresentable
不会有关联的类型要求,但仍然可以检索 rawValue
作为 Any
。那么在我们的代码中:
if let anyRawEnum = theEnum as? AnyRawRepresentable { // Able to cast to
let anyRawValue = anyRawEnum.anyRawValue // anyRawValue is of type Any
switch anyRawValue {
case let s as String:
return s
case let csc as CustomStringConvertible:
return csc.description
default:
return nil
}
}
这种解决方案可以广泛用于具有关联类型的任何类型的协议。我同样会将这个想法包含在我向 Swift 团队提出的扩展协议与协议一致性的提案中。
更新:None 以上选项自 Swift 起可用 4. 我没有收到关于为什么 Mirror
RawRepresentable
枚举不包含它的 rawValue
。至于选项 #2 和 #3,它们仍然在 Swift 未来版本的可能性范围内。 Swift 邮件列表和 Swift 团队的 Doug Gregor 撰写的 Generics Manifesto 文档中都提到了它们。
选项 #2 ("Allow for protocol types with defined associated types") 的正确术语是 generalized existentials。这将允许具有关联类型的协议可能自动 return Any
存在关联类型或允许如下语法:
anyEnum as? Any<RawRepresentable where .RawValue == String>
选项 #3 偶尔会在邮件列表中提到,这是一个通常被拒绝的请求功能,但这并不是说它不能包含在 Swift 的未来版本中。在泛型宣言中,它被称为 "Conditional conformances via protocol extensions"。虽然认识到它作为一项功能的强大功能,但令人遗憾的是,它还声明有效实施是 "nearly impossible."
2015 年 8 月 28 日更新: 这将在 Swift 2
中解决见Twitter response from Swift compiler developer
2015 年 10 月 23 日更新: 使用 Swift 2 个泛型,您仍然无法获得 rawValue。你这样做可以获得关联值。
原题:
我有一些generic reflection code写在swift。在该代码中,我无法获取基于枚举的属性的值。问题归结为我无法对 Any
类型的 属性 值执行 .rawValue
。 Swift 反射代码将 return 枚举的值作为 Any
类型。那么我怎样才能从 Any 到 AnyObject 这是枚举的原始值。
到目前为止我发现的唯一解决方法是使用协议扩展所有枚举。您可以在下面看到使用此解决方法的单元测试。
有没有办法在不向原始枚举添加代码的情况下解决这个问题?
对于我的反射代码,我需要 getRawValue
方法签名保持原样。
class WorkaroundsTests: XCTestCase {
func testEnumToRaw() {
let test1 = getRawValue(MyEnumOne.OK)
XCTAssertTrue(test1 == "OK", "Could nog get the rawvalue using a generic function")
let test2 = getRawValue(MyEnumTwo.OK)
XCTAssertTrue(test2 == "1", "Could nog get the rawvalue using a generic function")
let test3 = getRawValue(MyEnumThree.OK)
XCTAssertTrue(test3 == "1", "Could nog get the rawvalue using a generic function")
}
enum MyEnumOne: String, EVRawString {
case NotOK = "NotOK"
case OK = "OK"
}
enum MyEnumTwo: Int, EVRawInt {
case NotOK = 0
case OK = 1
}
enum MyEnumThree: Int64, EVRaw {
case NotOK = 0
case OK = 1
var anyRawValue: AnyObject { get { return String(self.rawValue) }}
}
func getRawValue(theEnum: Any) -> String {
// What can we get using reflection:
let mirror = reflect(theEnum)
if mirror.disposition == .Aggregate {
print("Disposition is .Aggregate\n")
// OK, and now?
// Thees do not complile:
//return enumRawValue(rawValue: theEnum)
//return enumRawValue2(theEnum )
if let value = theEnum as? EVRawString {
return value.rawValue
}
if let value = theEnum as? EVRawInt {
return String(value.rawValue)
}
}
var valueType:Any.Type = mirror.valueType
print("valueType = \(valueType)\n")
// No help from these:
//var value = mirror.value --> is just theEnum itself
//var objectIdentifier = mirror.objectIdentifier --> nil
//var count = mirror.count --> 0
//var summary:String = mirror.summary --> "(Enum Value)"
//var quickLookObject = mirror.quickLookObject --> nil
let toString:String = "\(theEnum)"
print("\(toString)\n")
return toString
}
func enumRawValue<E: RawRepresentable>(rawValue: E.RawValue) -> String {
let value = E(rawValue: rawValue)?.rawValue
return "\(value)"
}
func enumRawValue2<T:RawRepresentable>(rawValue: T) -> String {
return "\(rawValue.rawValue)"
}
}
public protocol EVRawInt {
var rawValue: Int { get }
}
public protocol EVRawString {
var rawValue: String { get }
}
public protocol EVRaw {
var anyRawValue: AnyObject { get }
}
不幸的是,目前这在 Swift 中看起来不太可能,但我已经考虑了您的问题一段时间,我将提出 3 种方法 Swift 团队可以帮助您解决这个问题。
修复枚举的镜像。最直接的解决方案是我相信您已经尝试过的解决方案。您正在尝试构建一个反射库,并且您想要反射一个
Any
值以查看它是否是一个枚举,如果是,您想要查看它是否具有原始值。rawValue
属性 应该 可以通过此代码访问:let mirror = reflect(theEnum) // theEnum is of Any type for i in 0..<mirror.count { if mirror[i].0 == "rawValue" { switch mirror[i].1.value { case let s as String: return s case let csc as CustomStringConvertible: return csc.description default: return nil } } }
但是,这不起作用。你会发现镜子的 count
为 0
。我真的认为这是 Swift 团队在实施 Swift._EnumMirror
时的疏忽,我将就此提交雷达。 rawValue
绝对是合法的属性。这是一个奇怪的场景,因为不允许枚举具有其他存储属性。此外,您的枚举声明从未明确符合 RawRepresentable
,也未声明 [=20=] 属性。编译器只是在您键入 enum MyEnum: String
或 : Int
或其他任何内容时推断。在我的测试中,似乎 属性 是在协议中定义还是关联类型的实例并不重要,它仍然应该是镜像中表示的 属性。
允许具有已定义关联类型的协议类型。 正如我在上面的评论中提到的,这是 Swift 中的一个限制,您不能强制转换到具有关联类型要求的协议类型。您不能简单地转换为
RawRepresentable
,因为 Swift 不知道rawValue
属性 将 return 是什么类型。RawRepresentable<where RawValue == String>
之类的语法是可以想象的,或者protocol<RawRepresentable where RawValue == String>
。如果可能的话,您可以尝试通过 switch 语句转换为类型,如:switch theEnum { case let rawEnum as protocol<RawRepresentable where RawValue == String>: return rawEnum.rawValue // And so on }
但这在 Swift 中没有定义。如果您只是尝试转换为 RawRepresentable
,Swift 编译器会告诉您只能在泛型函数中使用它,但当我查看您的代码时,这只会让您失望 -洞。通用函数 需要 在编译时输入信息才能工作,而这正是你在 Any
实例中所没有的。
Swift 团队可以将协议更改为更像通用 类 和结构。例如 MyGenericStruct<MyType>
和 MyGenericClass<MyType>
是合法的专用具体类型,您可以对其进行运行时检查并转换为。但是,Swift 团队可能有充分的理由不想使用协议来执行此操作。协议的特殊版本(即具有已知关联类型的协议引用)仍然不是具体类型。我不会为这种能力屏住呼吸。我认为这是我提出的解决方案中最薄弱的一个。
扩展协议以符合协议。 我真的以为我可以让这个解决方案为你工作,但遗憾的是没有。由于 Swift 的内置枚举镜像没有在
rawValue
上提供,我想为什么不实现我自己的通用镜像:struct RawRepresentableMirror<T: RawRepresentable>: MirrorType { private let realValue: T init(_ value: T) { realValue = value } var value: Any { return realValue } var valueType: Any.Type { return T.self } var objectIdentifier: ObjectIdentifier? { return nil } var disposition: MirrorDisposition { return .Enum } var count: Int { return 1 } subscript(index: Int) -> (String, MirrorType) { switch index { case 0: return ("rawValue", reflect(realValue.rawValue)) default: fatalError("Index out of range") } } var summary: String { return "Raw Representable Enum: \(realValue)" } var quickLookObject: QuickLookObject? { return QuickLookObject.Text(summary) } }
太棒了!现在我们所要做的就是将 RawRepresentable
扩展为 Reflectable
这样我们就可以首先转换 theEnum as Reflectable
(不需要关联类型)然后调用 reflect(theEnum)
给我们我们的 awesome自定义镜像:
extension RawRepresentable: Reflectable {
func getMirror() -> MirrorType {
return RawRepresentableMirror(self)
}
}
Compiler error: Extension of protocol 'RawRepresentable' cannot have an inheritance clause
哇哦?!我们可以扩展具体类型来实现新的协议,例如:
extension MyClass: MyProtocol {
// Conform to new protocol
}
从Swift2开始,我们可以扩展协议来给出功能的具体实现,例如:
extension MyProtocol {
// Default implementations for MyProtocol
}
我认为我们肯定可以扩展协议来实现其他协议,但显然不行!我看不出我们不能让 Swift 这样做的原因。我认为这非常符合 WWDC 2015 上讨论的面向协议的编程范例。实现原始协议的具体类型将免费获得新的协议一致性,但具体类型也可以自由定义自己的新协议方法的版本。我肯定会为此提交增强请求,因为我认为它可能是一个强大的功能。事实上,我认为它在您的反射库中可能非常有用。
编辑: 回想我对答案 2 的不满,我认为广泛使用这些协议有更优雅和现实的可能性,并且实际上结合了我对答案的想法3 关于扩展协议以符合新协议。这个想法是让相关类型的协议符合新协议,这些新协议可以检索原始类型擦除的属性。这是一个例子:
protocol AnyRawRepresentable {
var anyRawValue: Any { get }
}
extension RawRepresentable: AnyRawRepresentable {
var anyRawValue: Any {
return rawValue
}
}
以这种方式扩展协议本身并不是扩展继承。相反,它只是告诉编译器 "Wherever there is a type that conforms to RawRepresentable
, make that type also conform to AnyRawRepresentable
with this default implementation." AnyRawRepresentable
不会有关联的类型要求,但仍然可以检索 rawValue
作为 Any
。那么在我们的代码中:
if let anyRawEnum = theEnum as? AnyRawRepresentable { // Able to cast to
let anyRawValue = anyRawEnum.anyRawValue // anyRawValue is of type Any
switch anyRawValue {
case let s as String:
return s
case let csc as CustomStringConvertible:
return csc.description
default:
return nil
}
}
这种解决方案可以广泛用于具有关联类型的任何类型的协议。我同样会将这个想法包含在我向 Swift 团队提出的扩展协议与协议一致性的提案中。
更新:None 以上选项自 Swift 起可用 4. 我没有收到关于为什么 Mirror
RawRepresentable
枚举不包含它的 rawValue
。至于选项 #2 和 #3,它们仍然在 Swift 未来版本的可能性范围内。 Swift 邮件列表和 Swift 团队的 Doug Gregor 撰写的 Generics Manifesto 文档中都提到了它们。
选项 #2 ("Allow for protocol types with defined associated types") 的正确术语是 generalized existentials。这将允许具有关联类型的协议可能自动 return Any
存在关联类型或允许如下语法:
anyEnum as? Any<RawRepresentable where .RawValue == String>
选项 #3 偶尔会在邮件列表中提到,这是一个通常被拒绝的请求功能,但这并不是说它不能包含在 Swift 的未来版本中。在泛型宣言中,它被称为 "Conditional conformances via protocol extensions"。虽然认识到它作为一项功能的强大功能,但令人遗憾的是,它还声明有效实施是 "nearly impossible."