我应该在swift中使用枚举或类层次结构来处理错误吗?

时间:2015-10-03 01:01:25

标签: swift enums

在Swift 2中,任何符合ErrorType协议的类型都可以抛出并捕获。对我来说,有一个共同的错误层次结构并在多个地方重用它是有意义的。但是,Apple文档似乎促使开发人员使用枚举来处理错误。

例如,这个层次结构将让我捕获并处理一个常见的ValidationError,而不知道它的确切子类型。这也将允许应用程序的不同部分扩展ValidationError。

MyAppError
    ValidationError
        InvalidPathError
        WrongFileTypeError

混合不同风格的定义错误看起来不是一个好主意。那么,我应该围绕类层次结构或枚举建模所有错误处理吗?

1 个答案:

答案 0 :(得分:4)

TL;博士

枚举更短,写入更快,更容易理解潜在错误,编译器将确保您捕获所有错误。

全文

ErrorType本身只是一个空协议(隐藏属性_code : Int_domain : String,但Apple会照顾它。

我引用了Swift编程指南(link)

  

Swift枚举特别适合于对一组人进行建模   相关的错误条件,允许相关的值   有关错误性质的其他信息   传达。

要详细说明,enums允许您表达可能出错的内容。在进行错误处理时,您通常会遇到可能失败的特定条件(Swift会使用选项和类型安全性向您推动此方向)。 由于错误是不同的情况,您不应该真正需要多层继承(如果您确实需要在答案中添加详细信息)。使用枚举可以轻松表示错误。使用大型继承层次结构过于复杂。

假设您希望每个错误都有可以向用户显示的消息。您可以使用协议代替子类。

protocol MyAppError : ErrorType {
    var message: String { get }
}

将您的示例稍微进一步表示您将ValidationError表示为枚举(因为有许多验证错误)。

enum ValidationError : MyAppError {
    case InvalidPathError (String)
    case WrongFileTypeError (expectedFileType: String)

    var message: String {
        switch self {
        case .InvalidPathError(let invalidPath):
            return "\(invalidPath) is an invalid path"
        case .WrongFileTypeError(let expectedFileType):
            return "Expected type of \(expectedFileType)"
        }
    }
}

_

func myFileFunction(path: String) throws {
    guard let url = NSURL(string: path) else {
        throw ValidationError.InvalidPathError(path)
    }
    guard let data = NSDictionary(contentsOfURL: url) else {
        throw ValidationError.WrongFileTypeError(expectedFileType: ".plist")
    }
    print(data)
}


do {
    try myFileFunction("hi.jpg")
} catch ValidationError.InvalidPathError(let path) {
    print("Darn, had a bad path \(path)")
} catch ValidationError.WrongFileTypeError(let expectedType) {
    print("Darn, expected the type \(expectedType)")
}  catch (let error as MyAppError) {
    print("Darn, some old error \(error.message)")
}

编译器实际上知道该函数只会抛出ValidationErrors,因此如果您尝试捕获MyAppError它会向您发出警告。这是另一种/更好的方法。

do {
    try myFileFunction("hi.jpg")
} catch (let error as ValidationError) {
    switch error {
    case .WrongFileTypeError(let expectedType):
        print("Darn, expected the type \(expectedType)")
    case .InvalidPathError(let path):
        print("Darn, had a bad path \(path)")
    }
}

让我们与OO类/继承进行比较

class MyAppError : CustomStringConvertible {
    let message: String

    init(message: String) {
        self.message = message
    }

    var description: String {
        return message
    }
}

class ValidationError : MyAppError {

}

class InvalidPathError : ValidationError {
    let path: String

    init(message: String, path: String) {
        self.path = path
        super.init(message: message)
    }

    override var description: String {
        return "\(path) is an invalid path"
    }
}

class WrongFileTypeError : ValidationError {
    let expectedFileType: String

    init(message: String, expectedFileType: String) {
        self.expectedFileType = expectedFileType
        super.init(message: message)
    }

    override var description: String {
        return "Expected type of \(expectedFileType)"
    }
}

_

func myFileFunction(path: String) throws {
    guard let url = NSURL(string: path) else {
        throw InvalidPathError(path: path)
    }
    guard let data = NSDictionary(contentsOfURL: url) else {
        throw WrongFileTypeError(expectedFileType: ".plist")
    }
    print(data)
}

do {
    try myFileFunction("hi.jpg")
} catch (let error as InvalidPathError) {
    print("Darn, had a bad path \(error.path)")
} catch (let error as WrongFileTypeError) {
    print("Darn, expected the type \(error.expectedFileType)")
} catch (let error as MyAppError) {
    print("Some other error")
}

正如您所看到的,它可以工作,但创建错误类会增加很多包袱。我们也没有从ValidationError.WrongFileTypeErrorlet error as ValidationError获得自动命名空间。阅读此课程的所有人都知道InvalidPathErrorWrongFileTypeError是专门捕获的。与您知道ValidationError实例的枚举版本相比较,THE COMPILER会告诉您。