在Swift 2中,任何符合ErrorType协议的类型都可以抛出并捕获。对我来说,有一个共同的错误层次结构并在多个地方重用它是有意义的。但是,Apple文档似乎促使开发人员使用枚举来处理错误。
例如,这个层次结构将让我捕获并处理一个常见的ValidationError,而不知道它的确切子类型。这也将允许应用程序的不同部分扩展ValidationError。
MyAppError
ValidationError
InvalidPathError
WrongFileTypeError
混合不同风格的定义错误看起来不是一个好主意。那么,我应该围绕类层次结构或枚举建模所有错误处理吗?
答案 0 :(得分:4)
枚举更短,写入更快,更容易理解潜在错误,编译器将确保您捕获所有错误。
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.WrongFileTypeError
或let error as ValidationError
获得自动命名空间。阅读此课程的所有人都知道InvalidPathError
和WrongFileTypeError
是专门捕获的。与您知道ValidationError
实例的枚举版本相比较,THE COMPILER会告诉您。