考虑我编写的以下查找函数,即使用选项和可选绑定,如果在字典中找不到键,则报告消息
func lookUp<T:Equatable>(key:T , dictionary:[T:T]) -> T? {
for i in dictionary.keys {
if i == key{
return dictionary[i]
}
}
return nil
}
let dict = ["JO":"Jordan",
"UAE":"United Arab Emirates",
"USA":"United States Of America"
]
if let a = lookUp( "JO",dictionary:dict ) {
print(a) // prints Jordan
} else {
print("cant find value")
}
我已经重写了以下代码,但这一次,使用错误处理,保护语句,删除-> T?
并编写符合ErrorType
的枚举:
enum lookUpErrors : ErrorType {
case noSuchKeyInDictionary
}
func lookUpThrows<T:Equatable>(key:T , dic:[T:T])throws {
for i in dic.keys{
guard i == key else {
throw lookUpErrors.noSuchKeyInDictionary
}
print(dic[i]!)
}
}
do {
try lookUpThrows("UAE" , dic:dict) // prints united arab emirates
}
catch lookUpErrors.noSuchKeyInDictionary{
print("cant find value")
}
这两个功能都很好但是:
哪个功能可以提供更好的性能
哪个功能更安全&#34;
推荐使用哪种功能(基于利弊)
答案 0 :(得分:19)
这两种方法应该具有可比性。在引擎盖下,它们都做了非常相似的事情:返回一个带有被检查标志的值,并且只有当标志显示结果有效时才继续。对于选项,该标志是枚举(.None vs .Some),throws
该标志是一个隐式触发跳转到catch块的标志。
值得注意的是,你的两个函数不做同样的事情(如果没有键匹配则返回nil
,如果第一个键不匹配则返回另一个)。 / p>
如果性能至关重要,那么你可以通过消除不必要的键下标查找来编写它以便更快地运行:
func lookUp<T:Equatable>(key:T , dictionary:[T:T]) -> T? {
for (k,v) in dictionary where k == key {
return v
}
return nil
}
和
func lookUpThrows<T:Equatable>(key:T , dictionary:[T:T]) throws -> T {
for (k,v) in dic where k == key {
return v
}
throw lookUpErrors.noSuchKeyInDictionary
}
如果您在紧密循环中使用有效值对这两者进行基准测试,则它们的执行方式相同。如果您使用无效值对它们进行基准测试,则可选版本的速度大约是速度的两倍,因此可能实际上抛出的开销有一点点开销。但是,除非你真的在一个非常紧凑的循环中调用这个函数并预测很多失败,否则可能没有什么值得注意的。
他们都是相同安全的。在任何情况下都不能调用该函数,然后意外地使用无效结果。编译器强制您解包可选项,或者捕获错误。
在这两种情况下,您都可以绕过安全检查:
// force-unwrap the optional
let name = lookUp( "JO", dictionary: dict)!
// force-ignore the throw
let name = try! lookUpThrows("JO" , dic:dict)
这实际上归结为强迫呼叫者处理可能的故障的哪种方式更可取。
虽然这更主观,但我认为答案非常明确。你应该使用可选的而不是投掷的。
对于语言风格指导,我们只需要查看标准库。 Dictionary已经有一个基于键的查找(这个函数重复),它返回一个可选的。
可选择的更好选择的一个重要原因是,在这个函数中,只有一个可能出错。返回nil
时,仅出于一个原因,即字符中不存在该键。在任何情况下,函数都不需要指出它抛出的几个原因,并且返回nil
的原因应该对调用者来说是完全明显的。
另一方面,如果有多种原因,并且函数可能需要返回解释(例如,执行网络调用的函数,可能因网络故障或数据损坏而失败),则错误分类失败,可能包括一些错误文本将是一个更好的选择。
在这种情况下,可选的更好的另一个原因是失败甚至可能是预期/常见的。异常/意外故障的错误更多。返回可选项的好处是可以非常轻松地使用其他可选功能来处理它 - 例如可选链接(lookUp("JO", dic:dict)?.uppercaseString
)或使用nil-coalescing(lookUp("JO", dic:dict) ?? "Team not found"
)进行默认。相比之下,try/catch
设置和使用有点痛苦,除非调用者真的想要“异常”错误处理,即要做一堆东西,其中一些可能会失败,但是想要收集在底部处理故障。
答案 1 :(得分:7)
答案 2 :(得分:4)
如果您使用的是Swift 2.0,则可以使用这两个版本。第二个版本使用try / catch(与2.0一起引入),因此不向后兼容,这可能是一个不利于考虑的问题。
如果有任何性能差异,那么它将被忽略。
我个人最喜欢的是第一个,因为它很直接且可读性很好。如果我不得不对第二个版本进行维护,我会问自己为什么作者对这样一个简单的案例采用了try / catch方法。所以我宁愿感到困惑......
如果你有许多具有许多出口点(抛出)的复杂条件,那么我会选择第二个。但正如我所说,情况并非如此。