如果你重新抛出它们包装另一个异常,是否可以捕获所有异常类型?

Eri*_*tas 15 .net c# exception-handling

我知道你不应该编写缓存所有异常类型的代码.

try
{
  //code that can throw an exception
}
catch
{
   //what? I don't see no
}
Run Code Online (Sandbox Code Playgroud)

相反,你应该做更像下面的代码的事情,允许你没有想到的任何其他异常.

try
{
//code that can throw an exception
}
catch(TypeAException)
{
   //TypeA specific code
}

catch(TypeBException)
{
   //TypeB specific code
}
Run Code Online (Sandbox Code Playgroud)

但如果你用另一个异常包装它们,是否可以捕获所有异常类型?考虑下面的这个Save()方法我写的是Catalog类的一部分.我是否有任何错误捕获所有异常类型并返回单个自定义CatalogIOException,原始异常作为内部异常?

基本上我不希望任何调用代码必须知道可能在Save()方法中抛出的所有特定异常.他们只需要知道他们是否试图保存只读目录(CatalogReadOnlyException),目录无法序列化(CatalogSerializationException),或者是否存在写入文件的问题(CatalogIOException).

这是处理异常的好方法还是坏方法?

/// <summary>
/// Saves the catalog
/// </summary>
/// <exception cref="CatalogReadOnlyException"></exception>
/// <exception cref="CatalogIOException"></exception>
/// <exception cref="CatalogSerializingExeption"></exception>
public void Save()
{
    if (!this.ReadOnly)
    {
        try
        {
            System.Xml.Serialization.XmlSerializer serializer = new XmlSerializer(typeof(Catalog));
            this._catfileStream.SetLength(0); //clears the file stream
            serializer.Serialize(this._catfileStream, this);
        }
        catch (InvalidOperationException exp)
        {
            throw new CatalogSerializationException("There was a problem serializing the catalog", exp);
        }
        catch (Exception exp)
        {
            throw new CatalogIOException("There was a problem accessing the catalog file", exp);
        }
    }
    else
    {
        throw new CatalogReadOnlyException();
    }
}
Run Code Online (Sandbox Code Playgroud)

更新1

感谢到目前为止的所有回复.这听起来像是我不应该这样做的共识,我应该只是抓住异常,如果我真的与它们有关.在这个Save()方法的情况下,确实没有任何可能抛出的异常,我想在Save()方法本身中处理.大多数情况下,我只想通知用户他们无法保存的原因.

我认为我的真正问题是我使用异常作为向用户通知问题的一种方式,并且我告诉我如何创建和处理异常有点过多.因此,听起来似乎最好不要捕获任何异常并让UI层弄清楚如何通知用户,或者崩溃.它是否正确?考虑下面的Save Menu事件处理程序.

    private void saveCatalogToolStripMenuItem_Click(object sender, EventArgs e)
    {
        //Check if the catalog is read only
        if (this.Catalog.ReadOnly)
        {
            MessageBox.Show("The currently opened catalog is readonly and can not be saved");
            return;
        }

        //attempts to save
        try
        {
            //Save method doesn't catch anything it can't deal with directly
            this.Catalog.Save(); 
        }
        catch (System.IO.FileNotFoundException)
        {
            MessageBox.Show("The catalog file could not be found");
        }
        catch (InvalidOperationException exp)
        {
            MessageBox.Show("There was a problem serializing the catalog for saving: " + exp.Message);
        }
        catch (System.IO.IOException exp)
        {
            MessageBox.Show("There was a problem accessing the catalog file: " + exp.Message);
        }
        catch (Exception exp)
        {
            MessageBox.Show("There was a problem saving the catalog:" + exp.Message);
        }
    }
Run Code Online (Sandbox Code Playgroud)

更新2

还有一件事.如果Save()方法是公共API与内部代码的一部分,答案是否会发生变化?例如,如果它是公共API的一部分,那么我必须弄清楚并记录Save()可能抛出的所有可能的异常.如果知道Save()只能抛出我的三个自定义异常中的一个,那么这将会容易得多.

另外,如果Save()是公共API的一部分,那么安全性也不是一个问题吗?也许我想让API的消费者知道保存不成功,但我不希望通过让他们得到可能已经生成的异常来揭露有关Save()如何工作的任何信息.

Jas*_*Cav 7

做一个通用的catch-all和rethrowing作为一种新的异常并不能真正解决你的问题并且不会给你任何东西.

你真正需要做的是捕获你可以处理的异常,然后处理它们(在适当的级别 - 这是重新抛出可能有用的地方).所有其他异常都需要记录,以便您可以调试它们发生的原因,或者不应该首先发生(例如 - 确保验证用户输入等).如果你捕获所有异常,你将永远不会真正知道为什么你得到你正在获得的异常,因此,无法解决它们.

更新的响应

为了回答你的问题的更新(特别是你想如何处理保存案例),我的问题是 - 你为什么要使用例外来确定你的程序所采用的路径?例如,让我们采取"FileNotFoundException".显然有时会发生这种情况.但是,在保存(或做任何事情)文件之前,不要让问题发生并通知用户,为什么不首先检查文件是否可以找到.您仍然可以获得相同的效果,但您没有使用异常来控制程序流.

我希望这一切都有道理.如果您有任何其他问题,请与我们联系.

  • 检查文件是否存在并不能保证在读取文件时它不会消失,即使这是在下一行代码中.该检查可以让您在大多数情况下避免不必要的异常,但您仍需要进行错误处理以防万一. (2认同)

Gab*_*art 5

当您使用原始异常作为内部异常重新抛出时,将丢失原始堆栈跟踪,这是有价值的调试信息.

我有时会做你的建议,但我总是首先记录原始异常以保留堆栈跟踪.

  • 由于他正在包装原始异常,它如何失去它的堆栈轨道?它应该作为他的基础例外. (3认同)

Chr*_*sic -1

这是 .NET 中的标准做法以及如何处理异常,尤其是可恢复的异常。

编辑:我真的不知道为什么我被否决了,也许我比其他人更了解作者的意图。但我阅读代码的方式是,他将这些异常包装到他的自定义异常中,这意味着该方法的使用者有能力处理这些异常,并且使用者有责任处理错误处理。

DV实际上没有留下任何形式的实际争议。我坚持我的答案,这是完全可以接受的,因为消费者应该意识到这可能引发的潜在异常,并有能力处理它们,这通过异常的显式包装来显示。这还保留了原始异常,因此堆栈跟踪可用且底层异常可访问。