【问题标题】:Enum with instance-variable? (in Swift)枚举与实例变量? (在斯威夫特)
【发布时间】:2022-01-23 03:12:00
【问题描述】:

我使用enum(s) 定义所有自定义错误,例如:

public enum MyErrorEnum: String, LocalizedError {
    case FileNotFound = "Failed to find file."

    public var errorDescription: String? { rawValue }
}

但有些错误需要额外的context,例如在消息中添加文件路径。

不幸的是,由于 Swift 枚举不支持实例变量,我尝试了一些变通方法,例如:

private var KEY_CONTEXT: UInt8 = 0;

public enum MyErrorEnum: String, LocalizedError {
    case FileNotFound = "Failed to find file."

    public var errorDescription: String? { rawValue }

    public var context: String {
        return objc_getAssociatedObject(self as NSObject, &KEY_CONTEXT)
            as? String ?? "";
    }

    @discardableResult
    public mutating func withContext(_ value: String) -> Self {
        objc_setAssociatedObject(
            self as NSObject, &KEY_CONTEXT, value as NSString,
            .OBJC_ASSOCIATION_RETAIN_NONATOMIC);
        return self;
    }
}

请注意,上述内容不会在 Xcode 12 中引发任何编译和/或运行时错误,但只是不起作用。

后来,我收到通知 Xcode 13 引发以下编译错误: "Cannot convert value of type 'MyErrorEnum' to type 'NSObject' in coercion"

我也已经尝试过简单地将String 更改为实现ExpressibleByStringLiteral 的自定义StringWithContext 类,但即使这样也不起作用(可能是因为枚举的rawValue 以某种方式受到更改保护)。

有什么方法可以为所述枚举添加额外的变量?

【问题讨论】:

  • 现在有了比您之前的问题更多的上下文,看起来枚举根本不适合这个用例。使用具有两个属性的结构 - contexterrorType,其中 errorType 是一个简单的枚举。但是,如果每种情况都与不同类型的上下文相关联,请考虑使用associated values
  • @Sweeper 问题是干净的投掷和接球,无需重新投掷和复制/粘贴。 首先, 我想要没有复制/粘贴的消息(enum 非常独特,没有复制/粘贴),但不知何故需要有可变的上下文/详细信息。 其次, 我希望能够分别catch 每个case,而不是捕获整个struct 然后在catch 中执行switch,并且希望避免忘记重新抛出我们的案例不要处理。

标签: swift enums nserror


【解决方案1】:

,目前没有办法在Swift中直接给enum添加实例变量。

(在Java 中这很简单,因为enum 是另一个class)。

但是,如果你坚持使用 Swift 的enum,请使用默认值作为消息的关联枚举。

示例

只需从下方复制 LocalizedErrorEnum 并将其添加到您的项目中一次,然后使用关联枚举根据需要多次重复使用。

public protocol LocalizedErrorEnum: LocalizedError {
    var errorDescription: String? { get }
}

extension LocalizedErrorEnum {
    public var errorDescription: String? {
        if let current = Mirror(reflecting: self).children.first {
            let mirror = Mirror(reflecting: current.value);
            // Initial error description.
            let message = mirror.children.first?.value as? String
                ?? current.label ?? "Unknown-case";
            var context = "";
            // Iterate additional context.
            var i = 0;
            for associated in mirror.children {
                if i >= 1 {
                    if let text = associated.value as? String {
                        context += "\n  ";
                        if let label: String = associated.label {
                            context += "\(label): "
                        }
                        context += text;
                    }
                }
                i += 1;
            }
            return context.isEmpty ? message : (
                message + " {" + context + "\n}"
            );
        }
        return "\(self)";
    }
}

用法:

public enum MyErrorEnum: LocalizedErrorEnum {
    case FileNotFound(String = "Failed to find file.", file: String)
}

// ...

do {
    let path = "/path/to/file.txt";
    throw MyErrorEnum.FileNotFound(
        file: path
    );
} catch {
    print(error.localizedDescription);
}

输出:

Failed to find file. {
  file: /path/to/file.txt
}

另请参阅: Simplest way to throw custom error in Swift?

【讨论】:

    猜你喜欢
    • 2021-11-11
    • 2018-09-01
    • 1970-01-01
    • 2017-07-22
    • 2017-03-24
    • 2017-06-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多