我刚刚在try-catch上看到了一个问题,哪些人(包括Jon Skeet)说空的捕获块是一个真正的坏主意?为什么这个?难道没有一个空的陷阱不是错误的设计决定吗?
我的意思是,例如,有时您想从某个地方(Web服务,数据库)获得一些其他信息,而您实际上并不在乎是否会获得此信息。因此,您尝试获取它,并且如果发生任何事情,那没关系,我只需添加一个“ catch(忽略了异常){}”,仅此而已
我刚刚在try-catch上看到了一个问题,哪些人(包括Jon Skeet)说空的捕获块是一个真正的坏主意?为什么这个?难道没有一个空的陷阱不是错误的设计决定吗?
我的意思是,例如,有时您想从某个地方(Web服务,数据库)获得一些其他信息,而您实际上并不在乎是否会获得此信息。因此,您尝试获取它,并且如果发生任何事情,那没关系,我只需添加一个“ catch(忽略了异常){}”,仅此而已
Answers:
通常,空的try-catch是一个坏主意,因为您无声地吞下了错误条件,然后继续执行。有时这可能是正确的做法,但是通常这表明开发人员看到了异常,不知道该怎么办,因此使用空捕获来使问题静音。
这在编程上等同于将黑色胶带盖在发动机警告灯上。
我相信您如何处理异常取决于您正在使用的软件的哪一层:Rainforest中的异常。
在极少数情况下可以证明其合理性。在Python中,您经常会看到这种构造:
try:
result = foo()
except ValueError:
result = None
因此,可以执行以下操作(取决于您的应用程序):
result = bar()
if result == None:
try:
result = foo()
except ValueError:
pass # Python pass is equivalent to { } in curly-brace languages
# Now result == None if bar() returned None *and* foo() failed
在最近的.NET项目中,我不得不编写代码来枚举插件DLL,以找到实现特定接口的类。相关的代码位(在VB.NET中,抱歉)是:
For Each dllFile As String In dllFiles
Try
' Try to load the DLL as a .NET Assembly
Dim dll As Assembly = Assembly.LoadFile(dllFile)
' Loop through the classes in the DLL
For Each cls As Type In dll.GetExportedTypes()
' Does this class implement the interface?
If interfaceType.IsAssignableFrom(cls) Then
' ... more code here ...
End If
Next
Catch ex As Exception
' Unable to load the Assembly or enumerate types -- just ignore
End Try
Next
尽管即使在这种情况下,我也承认将故障记录在某个地方可能会有所改善。
通常会插入空的catch块,因为编码人员实际上并不知道他们在做什么。在我的组织中,一个空的catch块必须包含一个注释,说明为什么不进行任何例外处理是一个好主意。
与此相关的是,大多数人都不知道try {}块后面是否可以接上catch {}或finally {},只需要一个。
只有在确实存在异常的情况下才应抛出异常-发生超出正常范围的事情。一个空的catch块基本上说“正在发生坏事,但我不在乎”。这是一个坏主意。
如果您不想处理该异常,请使其向上传播,直到到达可以处理该异常的代码为止。如果没有什么可以处理该异常,则应将应用程序关闭。
catch (Exception) {}
是一个坏主意,但catch (SpecificExceptionType) {}
可能很好。程序员DID使用catch子句中的类型信息检查异常。
每乔希布洛赫 - 65项:不要忽视异常的有效的Java:
一个空的catch块实际上是在说:“我不想知道抛出什么错误,我将忽略它们。”
它与VB6的相似On Error Resume Next
,除了抛出异常后try块中的所有内容都将被跳过。
当某些东西随后破裂时,这无济于事。
这可能永远都不是对的,因为您正在悄悄地传递所有可能的异常。如果您期望某个特定的例外,则应该对其进行测试,如果不是您的例外,请重新抛出该例外。
try
{
// Do some processing.
}
catch (FileNotFound fnf)
{
HandleFileNotFound(fnf);
}
catch (Exception e)
{
if (!IsGenericButExpected(e))
throw;
}
public bool IsGenericButExpected(Exception exception)
{
var expected = false;
if (exception.Message == "some expected message")
{
// Handle gracefully ... ie. log or something.
expected = true;
}
return expected;
}
通常,您应该只捕获可以实际处理的异常。这意味着在捕获异常时要尽可能具体。捕获所有异常很少是一个好主意,而忽略所有异常几乎总是一个非常不好的主意。
我只能想到少数情况下,空的catch块有一些有意义的用途。如果通过重新尝试该动作来“处理”正在捕获的特定异常,则无需在catch块中执行任何操作。但是,记录发生异常的事实仍然是一个好主意。
另一个示例:CLR 2.0更改了终结器线程上未处理异常的处理方式。在2.0之前的版本中,该过程可以保留下来。在当前CLR中,如果终结器线程上有未处理的异常,则该过程终止。
请记住,只有在确实需要finalizer的情况下,才应实现finalizer,即使那样,您也应在finalizer中做尽可能少的事情。但是,如果终结器必须执行的任何工作都可能引发异常,则需要在两种弊端中取较小的一种。您是否由于未处理的异常而关闭应用程序?还是要在某种程度上处于未定义状态?至少从理论上讲,在某些情况下,后者可能是两个弊端中较小的一个。在那种情况下,空的catch块将阻止进程终止。
我的意思是,例如,有时您想从某个地方(Web服务,数据库)获得一些其他信息,而您实际上并不在乎是否会获得此信息。因此,您尝试获取它,并且如果发生任何事情,那没关系,我只需添加一个“ catch(忽略了异常){}”,仅此而已
因此,以您的示例为例,在这种情况下,这是一个坏主意,因为您正在捕获并忽略所有异常。如果您只是捕捉EInfoFromIrrelevantSourceNotAvailable
而忽略了它,那很好,但事实并非如此。您也忽略了ENetworkIsDown
,这可能重要也可能不重要。您忽略了ENetworkCardHasMelted
和EFPUHasDecidedThatOnePlusOneIsSeventeen
,这几乎可以肯定是很重要的。
如果将空的catch块设置为仅捕获(并忽略)您不重要的某些类型的异常,则不是问题。最好是抑制并静默忽略所有异常,而不必先检查它们是否是预期的/正常的/无关的情况,这是非常罕见的。
在某些情况下,您可能会使用它们,但是这种情况很少出现。我可能会使用的一种情况包括:
异常记录 根据上下文,您可能需要未处理的异常或消息。
循环出现技术状况,例如渲染或声音处理或列表框回调,其中行为本身将说明问题,抛出异常将阻碍您的工作,而记录异常可能只会导致1000条“失败到XXX”消息。
无法失败的程序,尽管它们至少仍应记录某些内容。
对于大多数winforms应用程序,我发现对于每个用户输入都只有一个try语句就足够了。我使用以下方法:(AlertBox只是一个快速的MessageBox.Show包装器)
public static bool TryAction(Action pAction)
{
try { pAction(); return true; }
catch (Exception exception)
{
LogException(exception);
return false;
}
}
public static bool TryActionQuietly(Action pAction)
{
try { pAction(); return true; }
catch(Exception exception)
{
LogExceptionQuietly(exception);
return false;
}
}
public static void LogException(Exception pException)
{
try
{
AlertBox(pException, true);
LogExceptionQuietly(pException);
}
catch { }
}
public static void LogExceptionQuietly(Exception pException)
{
try { Debug.WriteLine("Exception: {0}", pException.Message); } catch { }
}
然后,每个事件处理程序都可以执行以下操作:
private void mCloseToolStripMenuItem_Click(object pSender, EventArgs pEventArgs)
{
EditorDefines.TryAction(Dispose);
}
要么
private void MainForm_Paint(object pSender, PaintEventArgs pEventArgs)
{
EditorDefines.TryActionQuietly(() => Render(pEventArgs));
}
从理论上讲,您可以使用TryActionSilently,它可能更适合呈现调用,以便异常不会生成无数的消息。
如果您不知道在catch块中要做什么,则可以记录此异常,但不要将其留为空白。
try
{
string a = "125";
int b = int.Parse(a);
}
catch (Exception ex)
{
Log.LogError(ex);
}