gpt4 book ai didi

swift - 从泛型函数中的枚举获取 rawValue

转载 作者:行者123 更新时间:2023-12-02 12:12:09 25 4
gpt4 key购买 nike

2015 年 8 月 28 日更新:
这将在 Swift 2 中解决

Twitter response from Swift compiler developer

2015 年 10 月 23 日更新:
使用 Swift 2 泛型,您仍然无法获得 rawValue。你可以得到相关的值。

原问题:

我有一些 generic reflection code写得很快。在该代码中,我无法获取基于枚举的属性的值。问题归结为我无法执行 .rawValue在类型为 Any 的属性值上. Swift 反射代码将返回枚举类型的值 Any .那么如何从 Any 到 AnyObject,它是枚举的 rawValue。

到目前为止,我发现的唯一解决方法是使用协议(protocol)扩展所有枚举。您可以在下面看到使用此变通方法没问题的单元测试。

有没有办法在不向原始枚举中添加代码的情况下解决这个问题?

对于我的反射代码,我需要 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 中看起来不太可能,但是我已经考虑了您的问题一段时间,我将提出 Swift 团队可以帮助您解决此问题的 3 种方法。

  • 修复枚举的镜像。 最直接的解决方案是我确定您已经尝试过的解决方案。您正在尝试构建一个反射库,并且您想反射一个 Any value 以查看它是否是枚举,如果是,您想查看它是否具有原始值。 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
    }
    }
    }

  • 但是,这不起作用。你会发现镜子有一个 count0 .我真的认为这是 Swift 团队在实现 Swift._EnumMirror 时的疏忽。 ,我将就此事提交雷达报告。 rawValue绝对是合法属性(property)。这是一个奇怪的场景,因为枚举不允许具有其他存储属性。此外,您的枚举声明从未明确符合 RawRepresentable ,也不声明 rawValue属性(property)。编译器只会在您输入 enum MyEnum: String 时推断出或 : Int或者什么。在我的测试中,似乎属性是在协议(protocol)中定义还是关联类型的实例无关紧要,它仍然应该是镜像中表示的属性。
  • 允许具有已定义关联类型的协议(protocol)类型。 正如我在上面的评论中提到的,这是 Swift 中的一个限制,您不能将其转换为具有关联类型要求的协议(protocol)类型。你不能简单地转换到 RawRepresentable因为 Swift 不知道 rawValue 是什么类型的属性(property)会回来。语法如 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 团队可以将协议(protocol)更改为更像通用类和结构。例如 MyGenericStruct<MyType>MyGenericClass<MyType>是合法专用的具体类型,您可以对其进行运行时检查并强制转换为。然而,Swift 团队可能有充分的理由不想用协议(protocol)来做到这一点。协议(protocol)的专门版本(即具有已知关联类型的协议(protocol)引用)仍然不是具体类型。我不会为这种能力屏住呼吸。我认为这是我提出的解决方案中最弱的一个。
  • 扩展协议(protocol)以符合协议(protocol)。 我真的以为我可以让这个解决方案适合你,但可惜没有。由于 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)给我们我们很棒的定制镜子:
        extension RawRepresentable: Reflectable {
    func getMirror() -> MirrorType {
    return RawRepresentableMirror(self)
    }
    }

    Compiler error: Extension of protocol 'RawRepresentable' cannot have an inheritance clause



    哇!我们可以扩展具体类型来实现新协议(protocol),例如:
        extension MyClass: MyProtocol {
    // Conform to new protocol
    }

    从 Swift 2 开始,我们可以扩展协议(protocol)以给出函数的具体实现,例如:
        extension MyProtocol {
    // Default implementations for MyProtocol
    }

    我确信我们可以扩展协议(protocol)来实现其他协议(protocol),但显然不是!我看不出为什么我们不能让 Swift 做到这一点。我认为这非常符合 WWDC 2015 讨论的面向协议(protocol)的编程范式。实现原始协议(protocol)的具体类型将免费获得新的协议(protocol)一致性,但具体类型也可以自由定义自己的新协议(protocol)方法的版本。我肯定会为此提交增强请求,因为我认为它可能是一个强大的功能。事实上,我认为它在您的反射库中可能非常有用。

    编辑:回想我对答案 2 的不满,我认为广泛使用这些协议(protocol)有一种更优雅和现实的可能性,这实际上结合了我在答案 3 中关于扩展协议(protocol)以符合新协议(protocol)的想法。这个想法是让具有关联类型的协议(protocol)符合新协议(protocol),这些协议(protocol)可以检索原始类型删除的属性。下面是一个例子:
    protocol AnyRawRepresentable {
    var anyRawValue: Any { get }
    }

    extension RawRepresentable: AnyRawRepresentable {
    var anyRawValue: Any {
    return rawValue
    }
    }

    以这种方式扩展协议(protocol)本身不会扩展继承。相反,它只是对编译器说“只要有符合 RawRepresentable 的类型,请使用此默认实现使该类型也符合 AnyRawRepresentable。” 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
    }
    }

    这种解决方案可以广泛用于任何类型的具有关联类型的协议(protocol)。同样,我将这个想法包含在我向 Swift 团队提出的扩展具有协议(protocol)一致性的协议(protocol)的提案中。

    更新 :从 Swift 4 开始,上述选项均不可用。我没有收到关于为什么 Mirror 的回复。在 RawRepresentable枚举不包含其 rawValue .至于选项 #2 和 #3,它们仍然在 Swift future 版本的可能性范围内。它们已在 Swift 邮件列表和 Generics Manifesto 中提及。由 Swift 团队的 Doug Gregor 撰写的文档。

    选项#2(“允许具有已定义关联类型的协议(protocol)类型”)的正确术语是 generalized existentials .这将允许具有关联类型的协议(protocol)可能自动返回 Any其中有关联类型或允许使用如下语法:
    anyEnum as? Any<RawRepresentable where .RawValue == String>

    选项 #3 偶尔会在邮件列表中被提及,它是一个经常被拒绝的请求功能,但这并不是说它不能包含在 Swift 的 future 版本中。在泛型宣言中,它被称为 "Conditional conformances via protocol extensions" .在承认其作为一项功能的强大功能的同时,它也遗憾地指出,有效实现“几乎是不可能的”。

    关于swift - 从泛型函数中的枚举获取 rawValue,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31607629/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com