C#中实现自定义异常的行业标准最佳实践是什么?

85

在C#中实现自定义异常的行业标准最佳实践是什么?

我查看了谷歌并找到了很多建议,但我不知道哪些建议更为可信。

如果有人有权威文章的链接,那也会很有帮助。


1
类似于http://bit.ly/hVTSgp,我建议您通过进一步解释您所寻找的内容来缩小您的主题范围。 - Uwe Keim
我的老板希望我为我编写的程序创建一个自定义异常类,以便我们可以轻松地识别特定的错误。这些错误可能包括:特定Excel单元格中没有数据、超出范围的异常(可以使用标准异常处理),特定顺序未上传数据等。 - Darren Young
7
听起来你正在使用异常处理业务逻辑流程 - 可以在可能的情况下避免使用异常来处理预期问题。 - cjk
@ck - 说实话,我想你可能是对的。我现在正在回顾我的代码,并试着以不同的方式处理它们。谢谢。 - Darren Young
4个回答

71

创建自定义异常的标准做法是继承 Exception。然后你可以引入自己的属性/方法以及重载构造函数(如果适用)。

这里是一个基本的示例,演示了如何创建自定义异常ConnectionFailedException,它带有一个特定于该异常类型的额外参数。

[Serializable]
public class ConnectionFailedException : Exception
{
    public ConnectionFailedException(string message, string connectionString)
        : base(message)
    {
        ConnectionString = connectionString;
    }

    public string ConnectionString { get; private set; }
}

在应用程序中,这可以用于尝试连接到数据库的情况下。

try
{
    ConnectToDb(AConnString);
}
catch (Exception ex)
{
    throw new ConnectionFailedException(ex.Message, AConnString);
}

在适当情况下,由您来处理更高级别的 ConnectionFailedException 异常。

同时,请参考设计自定义异常自定义异常


为什么不仅捕获ConnectionFailedException类型的异常更有意义呢? - anar khalilov
@Anar 这个示例展示了你如何引发 ConnectionFailedException,期望是你应该在调用堆栈的更高层捕获它。你可以捕获从 ConnectToDb 抛出的一个更有意义的异常,而不是一个一般的 Exception,但这只是为了演示目的。 - James
抱歉。让我这样问一下,如果我们捕获ConnectionFailedException而不是Exception,会有什么区别吗? - anar khalilov
@Anar 我想说的是,ConnectionFailedException 是您引入的自定义异常。假设 ConnectToDb 方法正在连接到外部数据存储,例如 SQL Server/MySQL 等,则不会抛出 ConnectionFailedException。相反,它很可能会抛出供应商特定的异常,例如 SqlException。显然,最好捕获更具体的异常,但由于此示例与数据库无关,因此我们只捕获 Exception - James
1
@SalmanAbbas "这里有一个基本示例..." - 关键词是 "基本" 和 "示例". - James
显示剩余4条评论

15

以下是创建自定义异常的代码:

using System;
using System.Runtime.Serialization;

namespace YourNamespaceHere
{
    [Serializable()]
    public class YourCustomException : Exception, ISerializable
    {
        public YourCustomException() : base() { }
        public YourCustomException(string message) : base(message) { }
        public YourCustomException(string message, System.Exception inner) : base(message, inner) { }
        public YourCustomException(SerializationInfo info, StreamingContext context) : base(info, context) { }
    }
}

另请参阅:http://www.capprime.com/software_development_weblog/2005/06/16/CreatingACustomExceptionClassInC.aspx


2
就此而言,这几乎是使用 Visual Studio 中包含的“异常”代码片段所产生的内容。 - Jon Peterson
太好了。我在2005年写了这篇博客文章,但我认为当时有一些来自微软的指导,我基于这些指导编写了它,现在看起来这些指导已经通过代码片段被编码到VS中了。 - Michael Maddox
1
你的类不需要实现 ISerializable 接口(因为 Exception 已经实现了它,而你是从 Exception 继承的),同时你的最后一个构造函数(第四个)可以设置为 protected 访问修饰符(在 Exception 中也是这样)。 - bytedev

9

1
我使用自定义异常来传达错误的性质。
例如,我喜欢使用框架提供的“ArgumentNullException”来检查参数。然后,当我在调试器或错误日志中看到此错误时,我立即知道错误的性质,而无需进一步阅读。
另一方面,InvalidOperationException则可能意味着几乎任何事情。
自定义异常的替代方案是详细的错误消息。那也可以,但通过创建一个自定义异常,如ConnectionFailed,更有意义。然后消息本身可以提供更多细节。
在创建这样的自定义异常时,我不添加任何新属性。原因是如果您拥有错误记录器,您希望它适用于所有异常。如果您添加了特殊属性,则错误记录器将忽略它。例如,如果您使用MSTest,在运行测试并失败时,不会显示自定义属性。但是,如果坚持使用基类的Message属性,它将正常显示。
因此,子类化非常简单:
public class NavigationException : Exception{
    public NavigationException() {}
    public NavigationException(string msg) : base(msg) {}
    public NavigationException(string msg, Exception inner) : base(msg, inner) {}
}

这很简单,适用于任何错误记录器,当我看到它时,我知道这是导航问题,如果需要,我可以查看详细信息。
Greg

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接