【发布时间】:2017-06-08 09:50:15
【问题描述】:
在每个人都支持代码正确性之前,我意识到通常正确的做事方式是不使用 try/catch 进行控制流。不过,这有点极端,我想了解一下其他人在这种情况下会怎么做。
这是示例代码,不是实际代码,但它应该能够说明问题。如果有人想让我在这里为这些类型定义字典或一些模拟类,请告诉我,我会的。
public bool IsSomethingTrue1(string aString)
{
if (aDictionary.ContainsKey(aString)) //If the key exists..
{
var aStringField = aDictionary[aString].Field; //Get the field from the value.
if (aStringField != null) //If the field isn't null..
{
return aStringField.SubField != "someValue"; //Return whether a subfield isn't equal to a specific value.
}
}
return false; //If the key isn't found or the field is null, return false.
}
public bool IsSomethingTrue2(string aString)
{
try
{
return aDictionary[aString].Field.SubField != "someValue"; //Return whether the subfield isn't equal to a specific value.
}
catch (KeyNotFoundException) //The key wasn't in the dictionary..
{
return false;
}
catch (NullReferenceException) //The field (or the dictionary, if it is dynamically assigned rather than hardcoded) was null..
{
return false;
}
}
因此,在上面的示例中,第一个方法检查字典是否包含键,如果包含,则检查字段是否为空,如果子值不等于特定值,则返回 true值,否则返回 false。这避免了尝试/捕获,但是每次访问代码时,它都会检查字典以查看密钥是否存在(假设没有引擎盖下的缓存/等 - 如果有请告诉我) ,然后检查一个字段是否为空,然后是我们关心的实际检查。
在第二种方法中,我们使用try/catch,避免了多层控制逻辑。我们立即“开门见山”并尝试访问有问题的值,如果它以两种已知方式中的任何一种失败,它将返回 false。如果代码执行成功,可能返回true或false(同上)。
这两种方法都可以完成工作,但我的理解是,如果在大多数情况下没有出现任何问题,第一种方法的平均速度会更慢。第一种方法每次都必须检查所有内容,而第二种方法只需要处理出现问题的情况。堆栈上的异常将有一个额外的空间,以及一些用于跳转到不同捕获块/等的指令。这应该不会影响常规情况下的性能,除了一个堆栈变量之外没有任何问题,但是,如果我正确理解我在这里读到的内容: Do try/catch blocks hurt performance when exceptions are not thrown?
当然,对于这个确切的例子,差异可以忽略不计 - 但是,想象一个复杂的场景,在一个复杂的 if/then/else 树中进行大量检查,与带有失败条件列表的 try/catch 相比。我意识到是的,这种代码总是可以分解成更小的位或以其他方式重构,但为了论证,我们说它除了控制流之外不能更改(在某些情况下需要更改实际逻辑重新验证科学算法,例如,成本高昂/速度慢,需要尽量减少)。
教科书 我知道答案是使用第一种方法,但在某些情况下,第二种方法可能明显更快。
再一次,我知道这有点迂腐,但我真的很想确保我在重要的地方编写尽可能高效的代码,同时保持所有内容的可读性和可维护性(并且注释良好)。提前感谢您就此事向我提供意见!
【问题讨论】:
-
我不明白你为什么要这样做“return aDictionary[aString].Field.SubField != "someValue";"而不是 "return aStringField.SubField != "someValue"";这样你就不会检查字典两次
-
例外适用于“例外”情况。在我看来,字典中不存在的键并不例外。第一个例子更容易理解。
-
这有点太宽泛了,有点基于意见。 IBTL,但除此之外,还有更多的理由避免使用异常进行流控制,而不仅仅是性能。如果字典应该存在但为空,会发生什么?您唯一的线索是您返回“false”,这很可能来自尝试实际通过您的条件,也可能来自您隐藏的合法异常。
-
@Koenyn - 这是一个错误,感谢您发现它。
-
也许你会对
Dictionary.TryGetValue和 C# 6 中新的 null 条件运算符感兴趣,它可以让你做return aDictionary[aString]?.Field?.SubField ?? string.Empty != "someValue"
标签: c# if-statement exception-handling try-catch control-structure