**2015年8月28日更新:**将在Swift 2中解决此问题
参见Twitter response from Swift compiler developer
**更新10/23/2015:**使用Swift 2泛型仍然无法获取rawValue。您可以获取关联的值。
原问题:
我用swift写了一些generic reflection code。在那个代码中,我在获取基于枚举的属性值时遇到了麻烦。问题归结为我无法对Any
类型的属性值执行.rawValue
。Swift反射代码将返回Any
类型的枚举值。那么我如何从Any到AnyObject,它是枚举的rawValue。
到目前为止,我发现的唯一的变通方法是用协议扩展所有枚举。下面你可以看到一个使用这个变通方法的单元测试。
有没有什么方法可以解决这个问题,而不向原始枚举添加代码?
对于我的反射代码,我需要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 }
}
2条答案
按热度按时间kmynzznz1#
不幸的是,目前看来这在Swift中是不可能的,但我已经考虑了您的问题一段时间,我将提出Swift团队可以帮助您解决此问题的3种方法。
1.**修复枚举的镜像。**最直接的解决方案是一个我相信你已经尝试过的解决方案。你正在尝试构建一个反射库,你想反射一个
Any
值来看看它是否是一个枚举,如果是,你想看看它是否有一个原始值。rawValue
属性 * 应该 * 可以通过以下代码访问:然而,这是行不通的。你会发现镜像有一个
0
的count
。我真的认为这是Swift团队在实现Swift._EnumMirror
时的一个疏忽,我会就此提出一个雷达。rawValue
绝对是一个合法的属性。这是一个奇怪的场景,因为枚举不允许有其他存储属性。此外,你的枚举声明从来没有显式地符合RawRepresentable
,也没有声明rawValue
属性。编译器只是推断当你输入enum MyEnum: String
或: Int
或其他类型时。在我的测试中,似乎属性是在协议中定义的还是关联类型的示例都不重要,它仍然应该是镜像中表示的属性。1.**允许协议类型具有定义的关联类型。**正如我在上面的注解中提到的,Swift中的一个限制是,您不能强制转换为具有关联类型要求的协议类型。您不能简单地强制转换为
RawRepresentable
,因为Swift不知道rawValue
属性将返回什么类型。类似RawRepresentable<where RawValue == String>
的语法是可以想象的,或者protocol<RawRepresentable where RawValue == String>
。如果可以,您可以尝试通过switch语句强制转换为该类型,如下所示:但是Swift中没有定义。如果你只是尝试强制转换为
RawRepresentable
,Swift编译器会告诉你只能在泛型函数中使用它,但是当我查看你的代码时,这只会把你引入一个兔子洞。泛型函数在编译时需要类型信息才能工作,而这正是你在使用Any
示例时所没有的。Swift团队可以将协议修改为更像泛型类和结构体的类型,例如
MyGenericStruct<MyType>
和MyGenericClass<MyType>
是合法的专用具体类型,可以在运行时检查并强制转换为这些类型。Swift团队可能有充分的理由不想在协议上这么做。(例如,具有已知关联类型的协议引用)仍然不会是具体类型。我不会为这种能力屏住呼吸。我认为这是我提出的解决方案中最弱的。1.**扩展协议以符合协议。**我真的以为我可以让这个解决方案为您工作,但可惜没有。由于Swift的枚举内置镜像不能在
rawValue
上提供,我想为什么不实现我自己的通用镜像:很好!现在我们要做的就是将
RawRepresentable
扩展为Reflectable
,这样我们就可以先强制转换theEnum as Reflectable
(不需要关联类型),然后调用reflect(theEnum)
来给予我们的自定义镜像:编译器错误:协议“RawRepresentable”的扩展不能有继承子句
我们可以扩展具体的类型来实现新的协议,例如:
从Swift 2开始,我们可以扩展协议来给予函数的具体实现,例如:
我以为我们可以扩展协议来实现其他协议,但显然不行!我看不出为什么我们不能让Swift这样做。我认为这将非常适合WWDC 2015讨论的面向协议的编程范式。实现原始协议的具体类型将免费获得新的协议一致性。但是具体类型也可以自由定义新协议方法的自己版本。我肯定会为此提交一个增强请求,因为我认为这可能是一个强大的特性。事实上,我认为它在反射库中非常有用。
**编辑:**回想一下我对答案2的不满,我认为有一种更优雅和现实的可能性可以广泛地使用这些协议,这实际上结合了我在答案3中关于扩展协议以符合新协议的想法。其想法是让具有关联类型的协议符合新协议,新协议检索原始协议的类型擦除属性。下面是一个示例:
以这种方式扩展协议本身并不是扩展继承,而是对编译器说:“只要存在符合
RawRepresentable
的类型,就使用此默认实现使该类型也符合AnyRawRepresentable
。”AnyRawRepresentable
没有相关的类型要求,但是仍然可以将rawValue
作为Any
检索。在我们的代码中,则:这种解决方案可以广泛地应用于任何类型的协议,我也会在我给Swift团队的提案中加入这个想法,以便扩展协议,使其具有协议一致性。
更新:以上选项在Swift 4中都不可用。我没有收到关于为什么
RawRepresentable
枚举中的Mirror
不包含其rawValue
的回复。至于选项#2和#3,它们仍然在Swift未来版本的可能性范围内。Swift邮件列表和Swift团队的Doug Gregor撰写的Generics Manifesto文档中都提到了它们。选项#2(“允许协议类型具有定义的关联类型”)的适当术语是 generalized existentials。这将允许具有关联类型的协议可能自动地返回
Any
,其中存在关联类型或允许如下语法:选项#3偶尔会在邮件列表中被提及,这是一个经常被拒绝的请求特性,但这并不是说它不能被包含在Swift的未来版本中。在Generics Manifesto中,它被称为“通过协议扩展的条件一致性”。虽然承认它作为一个特性的强大,但它也遗憾地指出,有效的实现是“几乎不可能的”。
guicsvcw2#
好消息!
从Swift 5.6开始,您现在可以使用
any
关键字来执行此操作。它允许强制转换为具有关联类型的协议,如下所示: