“使用异常来控制流程”的代码是什么样的?我试图找到一个直接的C#示例,但不能。为什么不好?
由于
答案 0 :(得分:13)
根据定义,异常是在软件正常流程之外发生的事件。我的头顶快速示例是使用FileNotFoundException
来查看文件是否存在。
try
{
File.Open(@"c:\some nonexistent file.not here");
}
catch(FileNotFoundException)
{
// do whatever logic is needed to create the file.
...
}
// proceed with the rest of your program.
在这种情况下,您没有使用File.Exists()
方法来实现相同的结果,但没有异常的开销。
除了使用不当外,还存在与异常相关的开销,填充属性,创建堆栈跟踪等。
答案 1 :(得分:10)
以下代码捕获了一个可以轻松完全避免的异常。这使得代码更难以遵循,并且通常也会产生性能成本。
int input1 = GetInput1();
int input2 = GetInput2();
try
{
int result = input1 / input2;
Output("{0} / {1} = {2}", input1, input2, result);
}
catch (OverflowException)
{
Output("There was an overflow exception. Make sure input2 is not zero.");
}
此代码检查将抛出异常的情况,并在错误发生之前更正情况。这样就没有例外。代码更易读,性能很可能更好。
int input1 = GetInput1();
int input2 = GetInput2();
while (input2 == 0)
{
Output("input2 must not be zero. Enter a new value.");
input2 = GetInput2();
}
int result = input1 / input2;
Output("{0} / {1} = {2}", input1, input2, result);
答案 2 :(得分:9)
它大致等同于goto,除了“Exception”这个词更糟糕,并且开销更大。你告诉代码跳转到catch块:
bool worked;
try
{
foreach (Item someItem in SomeItems)
{
if (someItem.SomeTestFailed()) throw new TestFailedException();
}
worked = true;
}
catch(TestFailedException testFailedEx)
{
worked = false;
}
if (worked) // ... logic continues
正如您所看到的,它正在运行一些(补偿)测试;如果失败,则抛出异常,worked
将设置为false
。
当然,直接更新bool worked
要容易得多!
希望有所帮助!
答案 3 :(得分:5)
这是一个常见的:
public bool TryParseEnum<T>(string value, out T result)
{
result = default(T);
try
{
result = (T)Enum.Parse(typeof(T), value, true);
return true;
}
catch
{
return false;
}
}
答案 4 :(得分:2)
可能是我见过的最严重的违规行为:
// I haz an array...
public int ArrayCount(object[] array)
{
int count = 0;
try
{
while (true)
{
var temp = array[count];
count++;
}
}
catch (IndexOutOfRangeException)
{
return count;
}
}
答案 5 :(得分:2)
我目前正在使用第三方程序执行此操作。它们有一个“游标”接口(基本上是一个IEnumerable替代品),告诉程序你完成的唯一方法是引发一个异常。代码基本上看起来像:
// Just showing the relevant section
bool finished = false;
public bool IsFinished()
{
return finished;
}
// Using something like:
// int index = 0;
// int count = 42;
public void NextRecord()
{
if (finished)
return;
if (index >= count)
throw new APIProgramSpecificException("End of cursor", WEIRD_CONSTANT);
else
++index;
}
// Other methods to retrieve the current value
毋庸置疑,我讨厌 API - 但它是流量控制例外(以及疯狂的工作方式)的一个很好的例子。
答案 6 :(得分:1)
一个例子是使用异常从递归方法返回结果:
public void Search(Node node, object data)
{
if(node.Data.Equals(data))
{
throw new ResultException(node);
}
else
{
Search(node.LeftChild, data);
Search(node.RightChild, data);
}
}
出于某些原因,做这样的事情是个问题。
还有一些关于这个主题的例子和一些有趣的讨论here。
免责声明:上述代码改编自该维基页面上的第一个示例,将其转换为C#。
答案 7 :(得分:1)
我不喜欢C#,但你可以看到 try-catch-finally 语句和普通控制流语句 if-then-else 之间有一些相似之处。
当您throw
出现异常时,请考虑强制将控件传递给catch
子句。所以,如果你有
if (doSomething() == BAD)
{
//recover or whatever
}
您可以通过try-catch轻松地想到它:
try
{
doSomething();
}
catch (Exception e)
{
//recover or do whatever
}
关于异常的强大之处在于你不必在同一个体内改变程序的流程,你可以随时抛出一个异常,保证控制流会突然发散并到达捕获条款。这很强大但同时也很危险,因为你可以完成最后需要备份的操作,这就是finally
语句存在的原因。
此外,您还可以在没有有效使用条件的情况下对while
语句进行建模:
while (!finished)
{
//do whatever
}
可以成为
try
{
while (true)
{
doSomethingThatEventuallyWillThrowAnException();
}
}
catch (Exception e)
{
//loop finished
}
答案 8 :(得分:1)
合作伙伴开发的模块导致我们的应用程序需要很长时间才能加载。经过仔细研究,该模块正在寻找app启动时的配置文件。这本身并不太令人讨厌,但它的做法却非常糟糕:
对于app目录中的每个文件,它打开文件并尝试将其解析为XML。如果一个文件引发异常(因为它不是XML),它会捕获异常,压制它并尝试下一个文件!
当合作伙伴测试此模块时,他们在app目录中只有3个文件。 bonehead配置文件搜索对测试应用启动没有明显影响。当我们将它添加到我们的应用程序时,app目录中有100个文件,并且应用程序在启动时冻结了近一分钟。
要向伤口添加盐,模块正在搜索的配置文件的名称是预先确定的并且是常量。不需要任何文件搜索。
天才有其局限性。愚蠢是无限的。