C# Assert与特定抛出的异常

C# Assert与特定抛出的异常,c#,exception-handling,assert,C#,Exception Handling,Assert,我刚刚开始略读约翰·罗宾斯的《调试MS.NET2.0应用程序》,并对他的Debug.Assert(…)福音感到困惑 他指出,实现良好的断言在某种程度上存储了错误条件的状态,例如: Debug.Assert(i > 3, "i > 3", "This means I got a bad parameter"); 现在,就个人而言,他如此喜欢在没有实际合理的“业务逻辑”注释的情况下重新启动他的测试,这对我来说似乎很疯狂,也许“我IMO这只是浪费了开发时间。正确实现的异常让您清楚地了解了

我刚刚开始略读约翰·罗宾斯的《调试MS.NET2.0应用程序》,并对他的Debug.Assert(…)福音感到困惑

他指出,实现良好的断言在某种程度上存储了错误条件的状态,例如:

Debug.Assert(i > 3, "i > 3", "This means I got a bad parameter");

现在,就个人而言,他如此喜欢在没有实际合理的“业务逻辑”注释的情况下重新启动他的测试,这对我来说似乎很疯狂,也许“我IMO这只是浪费了开发时间。正确实现的异常让您清楚地了解了发生的情况。我看到太多的应用程序显示模糊的”断言失败:i<10“错误。我将断言视为一种临时解决方案。在我看来,任何断言都不应该出现在程序的最终版本中。在我的实践中,我使用断言进行快速而不准确的检查。代码的最终版本应该考虑到错误的情况,并相应地进行操作。如果有不好的事情发生,你有两个选择:处理它或离开它。如果传入了错误的参数,函数应该抛出一个带有有意义描述的异常。我看不出有重复验证逻辑的地方。

断言不用于参数检查。应始终进行参数检查(并严格按照您的文档和/或规范中指定的先决条件进行检查),必要时抛出
ArgumentOutOfRangeException

断言用于测试“不可能”的情况,即(在程序逻辑中)假设为真的情况。断言是为了告诉你这些假设是否因为任何原因而被打破


希望这有帮助

我使用显式检查,对公共和受保护的方法抛出异常,对私有方法抛出断言


通常,显式检查会防止私有方法看到错误的值。因此,断言实际上是在检查一个不可能的条件。如果断言触发,它会告诉我类上的一个公共例程中包含的验证逻辑存在缺陷。

断言与异常抛出之间存在通信方面

假设我们有一个具有Name属性和ToString方法的用户类

如果ToString是这样实现的:

Debug.Assert(i > 3, "i must be greater than 3 because of the flibbity widgit status");
if (i <= 3)
{
    throw new ArgumentOutOfRangeException("i", "i must be > 3 because... i=" + i.ToString());
}
public string ToString()
{
     Debug.Assert(Name != null);
     return Name;
}
public string ToString()
{
     if ( Name == null )
     {
          throw new InvalidOperationException("Name is null");
     }

     return Name;
}
它说名称永远不应该为空,如果为空,则用户类中存在错误

如果ToString是这样实现的:

Debug.Assert(i > 3, "i must be greater than 3 because of the flibbity widgit status");
if (i <= 3)
{
    throw new ArgumentOutOfRangeException("i", "i must be > 3 because... i=" + i.ToString());
}
public string ToString()
{
     Debug.Assert(Name != null);
     return Name;
}
public string ToString()
{
     if ( Name == null )
     {
          throw new InvalidOperationException("Name is null");
     }

     return Name;
}
如果Name为null,则表示调用者正在错误地使用ToString,并且应该在调用之前检查它

两者的实现

public string ToString()
{
     Debug.Assert(Name != null);
     if ( Name == null )
     {
          throw new InvalidOperationException("Name is null");
     }

     return Name;
}

表示如果Name为null,User类中就会有bug,但我们还是要处理它。(用户无需在呼叫前检查姓名。)我认为这是Robbins建议的安全措施。

可以捕获并吞咽异常,使错误对测试不可见。Debug.Assert不会发生这种情况

任何人都不应该有一个捕获所有异常的捕获处理程序,但人们无论如何都要这样做,有时这是不可避免的。如果您的代码是从COM调用的,则互操作层将捕获所有异常并将它们转换为COM错误代码,这意味着您将看不到未处理的异常。资产不受此影响

另外,当异常未处理时,更好的做法是进行小型转储。VB比C#更强大的一个方面是,当异常发生时,您可以使用异常过滤器捕捉一个小转储,并保持其余的异常处理不变。提供了一种从c#执行此操作的有用方法

关于资产的另一个注释。。。它们在对代码中的错误条件进行单元测试时表现不佳。有一个包装器关闭单元测试的断言是值得的。

这里是2美分

我认为最好的方法是同时使用断言和异常。如果断言语句可以从应用程序文本(定义、条件属性…)中轻松删除,那么这两种方法之间的主要区别是imho,而抛出的异常依赖于(特别是)更难删除的条件代码(带有预处理器条件的多段代码)

应正确处理每个应用程序异常,而只有在算法开发和测试期间才能满足断言

如果将空对象引用作为例程参数传递,并使用此值,则会出现空指针异常。事实上:为什么要写断言?在这种情况下是浪费时间。 但是在课堂常规中使用的私有类成员呢?当这些值设置在某个位置时,最好使用断言检查是否设置了空值。这只是因为当您使用该成员时,您会得到一个空指针异常,但您不知道该值是如何设置的。这会导致重新启动程序,在所有用于设置私有成员的入口点中断

异常更有用,但它们可能(imho)非常难以管理,并且有可能使用太多异常。它们需要额外的检查,可能是优化代码所不希望的。
就我个人而言,我只在代码需要深层catch控件(catch语句在调用堆栈中的位置很低)或函数参数没有在代码中硬编码时才使用异常。

在提供关于调试与断言的指导时,我考虑了这一点

您应该能够使用错误的输入、错误的状态、无效的操作顺序和任何其他可能的错误条件来测试您的类,并且断言应该永不跳闸。无论执行的输入或计算是什么,每个断言都在检查某些内容是否应始终为真

我得出了很好的经验法则:

  • 断言不能替代独立于配置正常运行的健壮代码。它们是互补的

  • 在单元测试运行期间,即使在