关于c#:核心库的异常架构

Exceptions architecture for core library

我目前正在做一个项目,这个项目分为几个部分,核心,用户界面等。在很难改变项目架构之前,我想知道在核心库中处理例外情况的最佳方法是什么?我的意思是,如何组织这些异常?例如,我可以通过有意义的消息抛出系统异常:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// Database implementation within Core library
class Database
{
    void Foo()
    {
        // ...
        if (Something())
            throw new InvalidDataException(message:"The something!");
        else
            throw new InvalidDataException(message:"It's not something!");
    }
}    

class UI
{
    void ShowDatabase()
    {
        var database = new Database();
        try
        {
            database.Foo();
        }
        catch (InvalidDataException e)
        {
            CleanUp();
            MessageBox.Show(e.ToString());
        }
    }
}

但是核心库不需要以任何方式处理用户。我说的对吗?好的,还有另一种方法。我可以将带有错误代码的系统异常作为异常消息抛出,这样UI层就可以为用户自己选择警告消息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
static class ErrorCode
{
   static string Something ="SomethingOccur";
   static string NotSomething ="NotSomethingOccur";
}

class Database
{
    void Foo()
    {
        // ...
        if (Something())
            throw new InvalidDataException(message:ErrorCode.Something);
        else
            throw new InvalidDataException(message:ErrorCode.NotSomething);
    }
}    

class UI
{
    void ShowDatabase()
    {
        var database = new Database();
        try
        {
            database.Foo();
        }
        catch (InvalidDataException e)
        {
            if (e.Message == ErrorCode.Something)
            {
                CleanUpSomthing();
                MessageBox.Show(Resources.SomethingMessage);
            }
            else if (e.Message == ErrorCode.NotSomething)
            {
                CleanUpSomethingElse();
                MessageBox.Show(Resources.NotSomethingMessage);
            }
        }
    }
}

现在它更灵活了,但我认为江户十一〔0〕看起来很难看。第三种方法是,单独实现任何情况下的异常:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
class SomethingException : Exception
{
    public SomethingException(string message = null, Exception inner = null) : base(message, inner) { }
}

class NotSomethingException : Exception
{
    public NotSomethingException(string message = null, Exception inner = null) : base(message, inner) { }
}

class Database
{
    void Foo()
    {
        // ...
        if (Something())
            throw new SomethingException()
        else
            throw new NotSomethingException();
    }
}    

class UI
{
    void ShowDatabase()
    {
        var database = new Database();
        try
        {
            database.Foo();
        }
        catch (SomethingException e)
        {
            CleanUpSomething();
            MessageBox.Show(Resources.SomethingMessage);
        }
        catch (NotSomethingException e)
        {
            CleanUpSomethingElse();
            MessageBox.Show(Resources.SomethingMessage);
        }
    }
}

它看起来更好,但在某个时刻每种情况都会有数百个例外。听起来很糟糕。

所以,问题是——在核心库中处理异常的最佳方法是什么?也许有什么最佳实践?

另外,对不起我的英语。


一般做法如下:

  • 在异常类型与特定情况匹配的任何情况下,都可以使用标准的.NET异常类(例如ArgumentNullExceptionInvalidOperationException。有很多.NET异常类,您需要对它们有一些了解,才能知道要抛出哪个类以及何时抛出。

  • 在与核心库的逻辑严格相连的错误情况下,您定义自己的异常类,并使用它们来抛出。

  • 您可能应该创建一个异常层次结构,其中基本异常类表示常规错误,更具体的异常类继承自这些异常。例如,您定义的基本异常类可能命名为:例如CalculationExcepion。然后定义从中继承的类——指定特定类型的计算异常。使用这种方法,库的用户将能够捕获基本异常(覆盖许多错误情况),或者根据他们的偏好处理特定的异常。

  • 您可以在异常中引入额外的属性,但是要注意像ErrorCode这样的属性,不要以一个可能包含50个不同错误代码的通用异常类结束—这对于核心库的用户来说很难处理。您可以在特定错误类型的有限数量的特殊情况下使用ErrorCode这样的属性,例如在执行GET请求时获得的HTTP代码:200、500、404和一些其他代码,但数量仍然有限。

  • 应该记录核心库中的公共方法和属性,说明它们抛出的异常类型以及这些异常的预期时间。