何时创建新的异常类型

时间:2012-05-18 09:53:26

标签: .net exception

何时创建新例外是合理的。在制定与.Net框架有关的新异常类型的良好设计决策之前,是否存在某些需要满足条件的清单?

例如考虑:

您有一个类,其方法返回一个整数,该整数对于方法的使用者来说并不重要,但提供了一些额外的信息。在该方法中可能出现的各种问题不依赖于类的用户如何调用此方法。假设一个是网络连接可能丢失,另一个是该方法试图调用的Web服务的API发生了变化。该方法的使用者无法以不同方式处理这些情况以尝试从异常中恢复。这些操作是否应该有两个不同的例外,或者是一个更普遍的例外(例如CommunicationFailureException),包括具有特定描述和内部异常的这两种情况?或者,当方法无法调用Web服务时,是否应抛出最初抛出的.Net异常?

我希望更多地了解创建新异常类型的原则,而不是仅仅涵盖这个特定示例。

6 个答案:

答案 0 :(得分:2)

如果您有关于异常的其他内容,请创建一个新的异常类型 - 这是现有异常未涵盖的内容。

如果您的应用程序具有某些特定的不变量,并且您需要进行通信,则它们无效 - 这可能是自定义异常类型的好地方。这允许您捕获特定于您的应用程序的异常。

答案 1 :(得分:1)

我认为在两种情况下创建自定义异常是有意义的。

  1. 如果您需要向例外添加更多信息。

  2. 如果您的异常调用者可能需要捕获该特定异常并以不同方式处理它。

  3. 否则,最好使用标准异常类。

答案 2 :(得分:0)

这个article提供了一些非常好的指示。

特别是声明的部分:

  

如果没有用,请尽量不要创建新的自定义例外   客户代码的信息。

答案 3 :(得分:0)

您可以在此页面查看一个小清单:http://docs.oracle.com/javase/tutorial/essential/exceptions/creating.html

此站点适用于Java,但概念是相同的。

答案 4 :(得分:0)

.Net Framework没有提供任何方法来调用方法Foo的例程可以区分Foo抛出的异常和由Foo抛出的异常。以Foo没有预料到并且没有处理的方式。 StackTrace可能提供线索,但由于内嵌等优化并不完全可靠。

因此,如果Foo将抛出任何应该对其调用者有某种意义的异常,它应该确保逃逸的那种类型的任何异常实际上都具有该含义。请注意.Net框架在这方面有些不足;例如,IEnumerator.MoveNext()的定义表示如果在枚举期间修改了集合,则应该抛出InvalidOperationException(在某些情况下建议代码可能想要创建集合的副本并枚举该集合)但是,如果由MoveNext()调用的例程由于其他原因引发了这样的异常,则不清楚会发生什么。

使用自定义异常通常是避免此类问题的好方法。如果你从一个类型的例程中抛出异常而不会被抛出用于任何其他目的,捕获它的代码可以确定异常意味着它认为它意味着什么。

答案 5 :(得分:0)

Oded提出了一个很好的观点。

我认为可以在本主题中添加的是对API的抽象。您需要创建一个异常包装器,为所有实现提供通用协定。我知道oded的回应涵盖了它,但我认为可以指出:

void Main()
{
    try
    {
        IUserRepository = //assume that IoC container provides specific implementation
    }
    catch (UserNotFoundException e)
    {
        Console.WriteLine(string.Format("User not found: {0}", e.Id));
    }
}


class User
{
    public int Id {get;set;}
}

class UserNotFoundException : Exception
{
    public int Id {get;private set;}

    public UserNotFoundException(int id, Exception innerException)
        :base(string.Format("User {0} could not be found", id), innerException)
    {
        Id = id;
    }
}

interface IUserRepository
{
    User GetUser(int id);
}

class XmlUserRepository : IUserRepository
{
    string _path;

    public XmlUserRepository(string path)
    {
        _path = path;
    }

    public User GetUser(int id)
    {
        try
        {
            //retrieving code that might throw IOException or XmlException
        }
        catch (Exception e)
        {
            //catching Exception is not a good thing to do, but for the sake of clarity I made this like that
            throw new UserNotFoundException(id, e);
        }
    }
}

class DbUserRepository : IUserRepository
{
    string _dbConnectionString;

    public DbUserRepository(string dbConnectionString)
    {
        _dbConnectionString = dbConnectionString;
    }

    public User GetUser(int id)
    {
        try
        {
            //retrieving code that might throw SqlException
        }
        catch (Exception e)
        {
            //catching Exception is not a good thing to do, but for the sake of clarity I made this like that
            throw new UserNotFoundException(id, e);
        }
    }
}