关于c#:Debug.Assert与特定的抛出异常

关于c#:Debug.Assert与特定的抛出异常

Debug.Assert vs. Specific Thrown Exceptions

我刚开始浏览John Robbins的"调试MS .Net 2.0应用程序",并且因为他对Debug.Assert(...)的传福音而感到困惑。

他指出,良好实现的Asserts会在某种程度上存储错误状态,例如:

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

现在,就个人而言,我似乎很疯狂,他如此喜欢在没有真正明智的"商业逻辑"评论的情况下重申他的考试,也许"因为flobittyjam widgitification过程,我必须永远不会发生i <= 3"。

所以,我认为我认为Asserts是一种低级别的"让我保护我的假设"的东西......假设一个人认为这是一个只需要在调试中做的测试 - 即你保护自己不受同事的影响和未来的程序员,并希望他们实际测试的东西。

但是我没有得到的是,他继续说除了正常的错误处理之外你还应该使用断言;现在我设想的是这样的:

1
2
3
4
5
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());
}

我通过Debug.Assert重复错误条件测试获得了什么?如果我们谈论一个非常重要的计算的仅调试双重检查,我想我会得到它...

1
2
double interestAmount = loan.GetInterest();
Debug.Assert(debugInterestDoubleCheck(loan) == interestAmount,"Mismatch on interest calc");

...但是我没有得到参数测试,这肯定值得检查(在DEBUG和Release版本中)......或者不是。我错过了什么?


断言不适用于参数检查。应始终进行参数检查(并且精确地根据文档和/或规范中指定的前提条件),并根据需要抛出ArgumentOutOfRangeException

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

希望这可以帮助!


断言与异常抛出有沟通方面。

假设我们有一个带有Name属性和ToString方法的User类。

如果ToString是这样实现的:

1
2
3
4
5
public string ToString()
{
     Debug.Assert(Name != null);
     return Name;
}

它表示Name应该永远不会为null,如果是,则User类中存在错误。

如果ToString是这样实现的:

1
2
3
4
5
6
7
8
9
public string ToString()
{
     if ( Name == null )
     {
          throw new InvalidOperationException("Name is null");
     }

     return Name;
}

它表示如果Name为null,调用者正在使用ToString,并且应该在调用之前检查它。

两者的实施

1
2
3
4
5
6
7
8
9
10
public string ToString()
{
     Debug.Assert(Name != null);
     if ( Name == null )
     {
          throw new InvalidOperationException("Name is null");
     }

     return Name;
}

如果Name为null,那么User类中会出现bug,但我们还是要处理它。 (用户在打电话之前不需要检查姓名。)我认为这是罗宾斯推荐的那种安全措施。


在提供关于测试问题的调试与断言的指导时,我已经考虑过这个漫长而艰难的过程。

您应该能够使用错误的输入,错误的状态,无效的操作顺序以及任何其他可能的错误条件来测试您的类,并且断言应该永远不会跳闸。每个断言都在检查一些事情应该始终为真,无论输入或计算是什么。

我已经达到了良好的经验法则:

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

  • 在单元测试运行期间,即使在输入无效值或测试错误条件时,断言也不应跳闸。代码应该处理这些条件而不会发生断言。

  • 如果断言跳闸(在单元测试中或在测试期间),则会对该类进行窃听。

  • 对于所有其他错误 - 通常是环境(网络连接丢失)或误用(调用者传递空值) - 使用硬检查和异常会更好,更容易理解。如果发生异常,则调用者知道它可能是他们的错。如果发生断言,则调用者知道它可能是断言所在代码中的错误。

    关于重复:我同意。我不明白为什么你会用Debug.Assert和异常检查来复制验证。它不仅会给代码增加一些噪音,而且还会让人感到困惑,因为这是一种重复的形式。


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

    通常,显式检查会保护私有方法无论如何都看不到错误的值。所以,断言正在检查一个应该是不可能的条件。如果一个断言触发,它告诉我在该类的一个公共例程中包含的验证逻辑中存在缺陷。


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

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

    此外,当异常未得到处理时,更好的做法是采取小型转储。 VB比C#更强大的一个领域是,当异常处于运行状态时,您可以使用异常过滤器来捕捉小型转储,并保持其余的异常处理不变。 Gregg Miskelly关于异常过滤器注入的博客文章提供了一种从c#执行此操作的有用方法。

    关于资产的另一个注意事项......他们在单元测试代码中的错误条件时表现不佳。有一个包装器来关闭单元测试的断言是值得的。


    IMO只是失去了开发时间。正确实施的例外可让您清楚了解发生的情况。我看到太多的应用程序显示模糊的"断言失败:我<10"错误。我认为断言是一种临时解决方案。在我看来,没有断言应该在程序的最终版本中。在我的实践中,我使用断言进行快速和脏检查。代码的最终版本应考虑错误的情况并相应地采取行动。如果发生了不好的事情,你有2个选择:处理它或离开它。如果传入了错误的参数,函数应抛出一个带有意义描述的异常。我认为验证逻辑重复没有任何意义。


    很好地使用Assert的示例:

    1
    2
    Debug.Assert(flibbles.count() < 1000000,"too many flibbles"); // indicate something is awry
    log.warning("flibble count reached" + flibbles.count()); // log in production as early warning

    我个人认为只有当你知道某些东西超出了理想的限制时才应该使用Assert,但是你可以确定它是合理安全的。在所有其他情况下(随意指出我没有想到的情况)使用异常来快速失败。

    对我来说,关键的权衡是你是想要关闭带有异常的实时/生产系统以避免损坏并使故障排除更容易,或者是否遇到了一个永远不允许在测试/调试版本中继续被忽视的情况但是被允许继续生产(当然记录警告)。

    比照http://c2.com/cgi/wiki?FailFast
    从java问题复制和修改:异常与断言


    这是2美分。

    我认为最好的方法是使用断言和异常。两个方法之间的主要区别,即imho,如果Assert语句可以从应用程序文本中轻松删除(定义,条件属性......),而抛出的Exception依赖于(tipically)一个更难删除的条件代码(具有预处理器条件的多段)。

    每个应用程序异常都应该正确处理,而断言只应在算法开发和测试期间得到满足。

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

    异常更有用,但它们可能(imho)非常重,无法管理,并且有可能使用太多异常。并且它们需要额外的检查,可能不希望优化代码。
    我个人只在代码需要深度捕获控件(调用堆栈中的catch语句非常低)或者代码中没有硬编码函数参数时才使用异常。


    推荐阅读

      浏览器调用linux命令?

      浏览器调用linux命令?,系统,信息,人工智能,软件,数据,首次,地址,代码,咨询,

      linux命令行调试代码?

      linux命令行调试代码?,环境,代码,信息,平台,程序,编辑,版本,步骤,体系结构,

      linux下单步调试命令?

      linux下单步调试命令?,信息,系统,代码,工程,地址,工具,工作,数据,管理,环境,l

      linux串口调试命令?

      linux串口调试命令?,设备,数据,信息,数字,系统,标准,通讯,软件,通用,状态,lin

      linux安装浏览器命令?

      linux安装浏览器命令?,官网,系统,平台,网站,管理,设备,咨询,工具,官方网站,

      linux浏览网页的命令?

      linux浏览网页的命令?,工作,系统,传播,地址,命令,环境,发行,信息,网络,基础,l

      linux启动浏览器命令?

      linux启动浏览器命令?,系统,情况,发行,不了,官网,环境,工具,传播,基础,软件,

      linux查询状态的命令?

      linux查询状态的命令?,系统,状态,信息,管理,数据,情况,命令,综合,电脑,工具,l

      linux命令的退出状态?

      linux命令的退出状态?,系统,状态,档案,软件,电脑,情况,异常,网络,命令,端口,

      linux默认浏览器命令?

      linux默认浏览器命令?,环境,系统,发行,工程,工具,浏览器,软件,情况,网站,管

      linux命令下载浏览器?

      linux命令下载浏览器?,软件,系统,官网,网站,工具,名称,中心,密码,时间,设计,l

      linux命令关浏览器?

      linux命令关浏览器?,系统,工作,工具,咨询,信息,平台,官方网站,管理,数据,电

      linux看文件状态命令?

      linux看文件状态命令?,状态,系统,服务,时间,信息,工作,电脑,管理,名称,网络,l

      linux调试终端命令?

      linux调试终端命令?,系统,工作,地址,首页,电脑,终端,命令,标准,信息,基础,求L

      linux脚本调试的命令?

      linux脚本调试的命令?,工作,系统,管理,命令,地址,标准,脚本,底部,代码,官网,l

      linux连接状态命令?

      linux连接状态命令?,网络,地址,状态,信息,系统,工具,工作,服务,位置,命令,lin

      linux命令调试模式?

      linux命令调试模式?,系统,工作,信息,地址,工程,命令,工具,环境,设备,地方,lin

      linux调试驱动的命令?

      linux调试驱动的命令?,系统,网络,官网,百度,地址,下来,第一,官方网站,软件,

      linux命令调试过程?

      linux命令调试过程?,代码,通用,地方,信息,系统,程序,进程,命令,编辑,断点,如

      linux命令行浏览页面?

      linux命令行浏览页面?,系统,工具,首页,信息,密码,终端,命令,网址,数据,传播,l