ChatGPT解决这个技术问题 Extra ChatGPT

什么时候应该使用 Debug.Assert()?

我已经做了大约一年的专业软件工程师,毕业于 CS 学位。我知道 C++ 和 C 中的断言有一段时间了,但直到最近才知道它们存在于 C# 和 .NET 中。

我们的生产代码不包含任何断言,我的问题是......

我应该开始在我们的生产代码中使用断言吗?如果是这样,什么时候使用最合适?这样做会更有意义吗

Debug.Assert(val != null);

或者

if ( val == null )
    throw new exception();
你设置的二分法就是线索。对于异常和断言,这不是一个非此即彼的问题,它的问题是防御性代码。什么时候做这是你想要理解的。
我曾经读到有人建议异常或其他崩溃方法适用于“我无法明智地从中恢复”的情况,另外,断言适用于“这永远不应该发生”的情况。但是,什么样的现实情况既满足了后者又满足了前者呢?来自断言在生产中保留的 Python 背景,我从来不理解在生产中关闭一些验证的 Java/C# 方法。我真正能看到的唯一情况是验证是否昂贵。
我个人对公共方法使用异常,对私有方法使用断言。

R
Rory MacLeod

Debugging Microsoft .NET 2.0 Applications 中,John Robbins 有很大一部分是关于断言的。他的主要观点是:

自由地断言。你永远不能有太多的断言。断言不能代替异常。例外涵盖了您的代码要求的内容;断言涵盖了它假设的东西。一个写得很好的断言不仅可以告诉你发生了什么和发生在哪里(比如一个例外),还可以告诉你为什么。异常消息通常很隐晦,需要您通过代码向后工作以重新创建导致错误的上下文。断言可以保留程序在错误发生时的状态。断言兼作文档,告诉其他开发人员您的代码所依赖的隐含假设。断言失败时出现的对话框允许您将调试器附加到进程,这样您就可以在堆栈中四处寻找,就好像您在那里放置了一个断点一样。

PS:如果您喜欢 Code Complete,我建议您继续阅读本书。我买它是为了了解如何使用 WinDBG 和转储文件,但前半部分包含了一些技巧,可以帮助避免首先出现错误。


+1 简洁而有用的摘要。非常直接适用。不过,对我来说最重要的是何时使用 Trace.Assert 与 Trace.Assert。即关于您何时希望/不希望它们出现在您的生产代码中的一些信息。
JonCoombs 是“Trace.Assert vs. Trace.Assert”的错字吗?
@thelem 也许乔恩的意思是 Debug.AssertTrace.Assert。后者在发布版本和调试版本中执行。
A
Anatoliy Nikolaev

Debug.Assert() 放在代码中您希望进行健全性检查以确保不变量的任何位置。当您编译发布版本时(即没有 DEBUG 编译器常量),对 Debug.Assert() 的调用将被删除,因此它们不会影响性能。

您仍应在调用 Debug.Assert() 之前引发异常。断言只是确保在您仍在开发时一切都按预期进行。


如果您在调用它之前仍然抛出异常,您能否澄清为什么要放入断言?还是我误解了你的回答?
@romkyns 您仍然必须包含它们,因为如果您不这样做,当您在发布模式下构建项目时,所有验证/错误检查都将消失。
@Oscar我认为这就是首先使用断言的全部意义......那么好吧,所以你将异常放在它们之前 - 那么为什么将断言放在后面呢?
@superjos:我不同意:MacLeod 的回答中的第 2 点指出您确实需要断言和异常,但不在同一个地方。在变量上抛出 NullRefEx 并在对其执行 Assert 之后是没有用的(在这种情况下,assert 方法永远不会显示一个对话框,这就是 assert 的全部意义所在)。 MacLeod 的意思是,在某些地方你需要一个例外,在其他地方,一个 Assert 就足够了。
解释我对别人答案的解释可能会变得混乱:) 无论如何,我和你在这些方面:你需要他们两个,你不应该把异常放在断言之前。我不确定“不在同一个地方”的含义。再次,拒绝解释,我只是陈述我的想法/偏好:在某些操作开始之前放置一个或多个断言来检查前置条件,或者在操作之后检查后置条件。除了断言之外,不管怎样,在它们之后,检查是否出现问题并需要抛出异常。
N
Nicholas Piasecki

FWIW ...我发现我的公共方法倾向于使用 if () { throw; } 模式来确保正确调用该方法。我的私有方法倾向于使用 Debug.Assert()

这个想法是,使用我的私有方法,我是受控制的,所以如果我开始使用不正确的参数调用我自己的私有方法之一,那么我在某处打破了自己的假设——我不应该得到进入那个状态。在生产中,理想情况下,这些私有断言应该是不必要的工作,因为我应该保持我的内部状态有效和一致。与提供给公共方法的参数相比,任何人都可以在运行时调用它:我仍然需要通过抛出异常来强制执行参数约束。

此外,如果某些东西在运行时不起作用(网络错误、数据访问错误、从第三方服务检索到的错误数据等),我的私有方法仍然会引发异常。我的断言只是为了确保我没有打破我自己对对象状态的内部假设。


这是对良好实践的非常清晰的描述,它对所提出的问题给出了非常合理的答案。
C
Community

来自Code Complete

防御性编程 8.2 断言 断言是在开发过程中使用的代码——通常是例程或宏——允许程序在运行时检查自身。当断言为真时,这意味着一切都按预期运行。当它为假时,这意味着它在代码中检测到意外错误。例如,如果系统假设客户信息文件的记录永远不会超过 50,000 条,则程序可能包含记录数小于或等于 50,000 条的断言。只要记录数小于或等于 50,000,断言就会保持沉默。但是,如果它遇到超过 50,000 条记录,它会大声“断言”程序中存在错误。断言在大型复杂程序和高可靠性程序中特别有用。它们使程序员能够更快地清除不匹配的接口假设、修改代码时出现的错误等等。一个断言通常需要两个参数:一个布尔表达式,描述假设为真,如果不是,则显示消息。 (...) 通常,您不希望用户在生产代码中看到断言消息;断言主要用于开发和维护期间。断言通常在开发时编译到代码中,并从代码中编译出来用于生产。在开发过程中,断言会清除矛盾的假设、意外情况、传递给例程的错误值等等。在生产过程中,它们是从代码中编译出来的,因此断言不会降低系统性能。


那么,如果在生产中遇到的客户信息文件包含超过 50,000 条记录会怎样?如果断言是从生产代码中编译出来的,并且这种情况不做其他处理,这不会带来麻烦吗?
@DavidRR 确实是的。但是一旦生产发出问题并且一些开发人员(可能不太了解此代码)调试问题,断言将失败并且开发人员将立即知道系统没有按预期使用。
答案中的链接已失效 - “无法访问此站点 | cc2e.com 响应时间过长。”
J
Justin R.

使用断言检查开发人员假设和异常检查环境假设。


M
Michael Freidgeim

如果我是你,我会这样做:

Debug.Assert(val != null);
if ( val == null )
    throw new exception();

或避免重复条件检查

if ( val == null )
{
    Debug.Assert(false,"breakpoint if val== null");
    throw new exception();
}

这是如何解决问题的?这样 debug.assert 就变得毫无意义了。
不,它没有 - 它在抛出异常之前的那一点中断代码。如果您在代码中的其他地方有 try/catch,您甚至可能不会注意到异常!
+1 我遇到了很多问题,人们只会尝试/捕获异常而不做任何事情,因此跟踪错误是一个问题
我想在某些情况下您可能想要这样做,但您永远不应该捕获一般异常!
@MarkIngram -1 给你的答案,+1 给你的评论证明它的合理性。对于某些特殊情况,这是一个不错的技巧,但对于所有验证来说,这似乎是一件奇怪的事情。
J
Joe

如果您希望在您的生产代码中使用 Asserts(即发布版本),您可以使用 Trace.Assert 而不是 Debug.Assert。

这当然会增加您的生产可执行文件的开销。

此外,如果您的应用程序在用户界面模式下运行,则默认情况下会显示“断言”对话框,这可能会让您的用户感到有些不安。

您可以通过删除 DefaultTraceListener 来覆盖此行为:查看 MSDN 中 Trace.Listeners 的文档。

总之,

大量使用 Debug.Assert 来帮助捕获 Debug 构建中的错误。

如果您在用户界面模式下使用 Trace.Assert,您可能希望删除 DefaultTraceListener 以避免让用户感到不安。

如果您正在测试的条件是您的应用程序无法处理的情况,您最好抛出异常,以确保执行不会继续。请注意,用户可以选择忽略断言。


+1 指出了 Debug.Assert 和 Trace.Assert 之间的关键区别,因为 OP 专门询问了生产代码。
u
user19113

断言用于捕获程序员(您的)错误,而不是用户错误。仅当用户不可能触发断言时才应使用它们。例如,如果您正在编写 API,则不应使用断言来检查 API 用户可以调用的任何方法中的参数是否不为空。但它可以用于不作为 API 的一部分公开的私有方法中,以断言您的代码在不应该传递空参数时永远不会传递。

当我不确定时,我通常更喜欢异常而不是断言。


S
StuartLC

简而言之

Asserts 用于保护和检查按合同设计的约束,即确保您的代码、对象、变量和参数的状态在边界和限制内运行 您的预期设计。

断言应仅用于调试和非生产版本。在发布版本中,编译器通常会忽略断言。

断言可以检查系统控制的错误/意外情况

断言不是用于第一线验证用户输入或业务规则的机制

断言不应用于检测意外的环境条件(超出代码的控制范围),例如内存不足、网络故障、数据库故障等。虽然很少见,但这些情况是可以预料的(并且您的应用程序代码无法修复问题)例如硬件故障或资源耗尽)。通常,异常会被抛出——您的应用程序可以采取纠正措施(例如重试数据库或网络操作,尝试释放缓存的内存),或者如果无法处理异常则优雅地中止。

一个失败的断言对你的系统来说应该是致命的——也就是说,与异常不同,不要试图捕捉或处理失败的断言——你的代码在意想不到的领域运行。堆栈跟踪和故障转储可用于确定出了什么问题。

断言有很大的好处:

帮助查找缺少的用户输入验证,或更高级别代码中的上游错误。

代码库中的断言清楚地将代码中的假设传达给读者

断言将在运行时在调试版本中进行检查。

一旦代码经过详尽的测试,将代码重新构建为 Release 将消除验证假设的性能开销(但如果需要,以后的 Debug 构建将始终恢复检查)。

... 更多详情

Debug.Assert 表示程序控制范围内的代码块的其余部分已假定有关状态的条件。这可以包括提供的参数的状态、类实例成员的状态,或者方法调用的返回在其约定/设计范围内。通常,断言应该使用所有必要的信息(堆栈跟踪、崩溃转储等)使线程/进程/程序崩溃,因为它们表明存在未设计用于的错误或未考虑的条件(即不要尝试捕获或处理断言失败),一个可能的例外是断言本身可能比错误造成更大的损害(例如,当飞机进入潜艇时,空中交通管制员不想要 YSOD,尽管是否应该部署调试版本是没有实际意义的生产 ...)

什么时候应该使用 Asserts?

在系统、库 API 或服务中,假设对函数或类状态的输入是有效的(例如,当已经对系统表示层中的用户输入进行验证时,业务和数据tier 类通常假定已经对输入进行了空值检查、范围检查、字符串长度检查等)。

常见的断言检查包括无效假设将导致空对象取消引用、零除数、数字或日期算术溢出以及一般带外/不是为行为而设计的(例如,如果使用 32 位 int 来模拟人类的年龄,谨慎的做法是断言年龄实际上在 0 到 125 左右之间 - 不是为 -100 和 10^10 的值设计的)。

.Net 代码合同
在 .Net 堆栈中,Code Contracts 可以通过 Debug.Assert 使用 in addition to, or as an alternative to。代码契约可以进一步形式化状态检查,并且可以帮助在编译时(或之后不久,如果在 IDE 中作为背景检查运行)检测违反假设的情况。

可用的合同设计 (DBC) 检查包括:

Contract.Requires - 约定的前提条件

Contract.Ensures - 约定的后置条件

不变量 - 表示关于对象在其生命周期中所有点的状态的假设。

Contract.Assumes - 在调用非 Contract 修饰方法时安抚静态检查器。


不幸的是,自从 MS 停止开发代码合同以来,它几乎已经死了。
我不禁觉得很幽默,以“简而言之”开头的答案是对该问题提出的最长答案的竞争者。 (公平地说,它让我想起了我自己倾向于保持冗长模式。)
C
Chris

在我的书中几乎从来没有。在绝大多数情况下,如果您想检查一切是否正常,则如果不正常则抛出。

我不喜欢的是它使调试版本在功能上与发布版本不同。如果调试断言失败但该功能在发布中有效,那么这有什么意义呢?如果断言者早已离开公司并且没有人知道那部分代码,那就更好了。然后你必须打发一些时间来探索这个问题,看看它是否真的是一个问题。如果这是一个问题,那么为什么不是首先扔的人?

对我来说,这表明通过使用 Debug.Asserts 您将问题推迟到其他人,自己处理问题。如果某些事情应该是这种情况,而事实并非如此,那么就抛出。

我想可能存在性能关键场景,您希望优化您的断言并且它们在那里很有用,但是我还没有遇到这样的场景。


尽管您强调了一些经常引起关注的问题,但您的回答值得一些优点,它们会中断调试会话以及误报的机会。但是,您缺少一些细微之处并且正在编写“优化断言”-这只能基于认为抛出异常和执行 debug.assert 是相同的。并非如此,它们具有不同的目的和特征,正如您在一些赞成的答案中所看到的那样。矮
+1 for “我不喜欢的是它使调试构建在功能上与发布构建不同。如果调试断言失败但功能在发布中有效,那么这有什么意义?”在 .NET 中,System.Diagnostics.Trace.Assert() 在发布版本和调试版本中执行。
d
devlord

根据 IDesign Standard,您应该

断言每一个假设。平均而言,每五行是一个断言。

using System.Diagnostics;

object GetObject()
{...}

object someObject = GetObject();
Debug.Assert(someObject != null);

作为免责声明,我应该提一下,我发现实施此 IRL 并不实用。但这是他们的标准。


看起来 Juval Lowy 喜欢引用自己的话。
答案中的链接已失效 - “500 Internal Server Error”。
D
Derek Park

仅在您希望为发布版本删除检查的情况下使用断言。请记住,如果您不在调试模式下编译,您的断言将不会触发。

鉴于您的 check-for-null 示例,如果这是在仅限内部的 API 中,我可能会使用断言。如果它在公共 API 中,我肯定会使用显式检查并抛出。


在 .NET 中,可以使用 System.Diagnostics.Trace.Assert() 在发布(生产)构建中执行断言。
代码分析规则 CA1062: Validate arguments of public methods 要求在以下情况下检查 null 的参数:“外部可见方法取消引用其引用参数之一,而不验证该参数是否为 null ." 在这种情况下,方法或属性应该抛出 ArgumentNullException
P
Pang

所有断言都应该是可以优化为的代码:

Debug.Assert(true);

因为它正在检查您已经假设为真的事情。例如:

public static void ConsumeEnumeration<T>(this IEnumerable<T> source)
{
  if(source != null)
    using(var en = source.GetEnumerator())
      RunThroughEnumerator(en);
}
public static T GetFirstAndConsume<T>(this IEnumerable<T> source)
{
  if(source == null)
    throw new ArgumentNullException("source");
  using(var en = source.GetEnumerator())
  {
    if(!en.MoveNext())
      throw new InvalidOperationException("Empty sequence");
    T ret = en.Current;
    RunThroughEnumerator(en);
    return ret;
  }
}
private static void RunThroughEnumerator<T>(IEnumerator<T> en)
{
  Debug.Assert(en != null);
  while(en.MoveNext());
}

在上面,有三种不同的方法来处理空参数。第一个接受它是允许的(它什么也不做)。第二个抛出异常以供调用代码处理(或不处理,导致错误消息)。第三个假设它不可能发生,并断言它是这样的。

在第一种情况下,没有问题。

在第二种情况下,调用代码存在问题 - 它不应该调用带有 null 的 GetFirstAndConsume,因此它会返回异常。

在第三种情况下,此代码存在问题,因为在调用它之前应该已经检查过 en != null,因此它不是一个错误。或者换句话说,它应该是理论上可以优化为 Debug.Assert(true) 的代码,即 en != null 应该始终是 true


那么,在第三种情况下,如果 en == null 在生产中会发生什么?您是否可能说en == null在生产中永远不会发生(因为程序已经过彻底调试)?如果是这样,那么 Debug.Assert(en != null) 至少可以作为评论的替代品。当然,如果将来进行更改,它也继续具有检测可能的回归的价值。
@DavidRR,我确实在断言它永远不能为空,代码中的断言也是如此,因此得名。我当然可能是错的,或者被改变弄错了,这就是 assert 调用的价值。
在发布版本中删除了对 Debug.Assert() 的调用。因此,如果您错了,在第三种情况,您将不会在生产中知道它(假设在生产中使用 Release 构建)。但是,第一种情况和第二种情况的行为在 Debug 和 Release 版本中是相同的。
@DavidRR,仅当我认为它不会发生时才适用,因为它再次是对事实的断言,而不是对状态的检查。当然,如果有断言,有它会捕获的错误,但在测试中从未遇到过这种情况,那也是毫无意义的。
N
Noctis

我想我会再添加四个案例,其中 Debug.Assert 可能是正确的选择。

1) 我在这里没有看到的是 Asserts 在自动化测试期间可以提供的额外概念覆盖。举个简单的例子:

当作者修改了一些更高级别的调用者时,作者认为他们已经扩展了代码的范围以处理其他场景,理想情况下(!)他们将编写单元测试来覆盖这个新条件。然后可能是完全集成的代码似乎可以正常工作。

然而,实际上已经引入了一个细微的缺陷,但在测试结果中没有发现。在这种情况下,被调用者变得不确定,只是碰巧提供了预期的结果。或者它可能产生了一个未被注意到的舍入错误。或者导致在其他地方同样抵消的错误。或者不仅授予请求的访问权限,还授予不应授予的其他权限。等等。

此时,被调用方中包含的 Debug.Assert() 语句与单元测试驱动的新案例(或边缘案例)相结合,可以在测试期间提供宝贵的通知,即原作者的假设已失效,代码不应无需额外审查即可发布。带有单元测试的断言是完美的合作伙伴。

2)此外,一些测试编写起来很简单,但考虑到初始假设,成本高且不必要。例如:

如果一个对象只能从某个安全入口点访问,是否应该从每个对象方法对网络权限数据库进行额外查询以确保调用者具有权限?肯定不是。也许理想的解决方案包括缓存或其他一些功能扩展,但设计不需要它。当对象已附加到不安全的入口点时,Debug.Assert() 将立即显示。

3) 接下来,在某些情况下,您的产品在以发布模式部署时可能对其全部或部分操作没有有用的诊断交互。例如:

假设它是一个嵌入式实时设备。当遇到格式错误的数据包时抛出异常并重新启动会适得其反。相反,设备可能会受益于尽力而为的操作,甚至会在其输出中呈现噪声。在发布模式下部署时,它也可能没有人机界面、日志记录设备,甚至根本无法被人类物理访问,并且最好通过评估相同的输出来提供对错误的感知。在这种情况下,自由断言和彻底的预发布测试比异常更有价值。

4)最后,一些测试是不必要的,只是因为被调用者被认为是非常可靠的。在大多数情况下,可重用的代码越多,就越努力使其可靠。因此,对于来自调用者的意外参数,异常是常见的,但对于来自被调用者的意外结果,断言是常见的。例如:

如果核心 String.Find 操作声明它将在未找到搜索条件时返回 -1,则您可以安全地执行一项操作而不是三项操作。但是,如果它实际返回 -2,您可能没有合理的行动方案。将更简单的计算替换为单独测试 -1 值的计算是没有帮助的,并且在大多数发布环境中使用测试来乱扔代码以确保核心库按预期运行是不合理的。在这种情况下,断言是理想的。


T
Teoman shipahi

引自 The Pragmatic Programmer: From Journeyman to Master

让断言保持打开状态 编写编译器和语言环境的人发布了关于断言的常见误解。它是这样的:断言给代码增加了一些开销。因为它们会检查不应该发生的事情,所以它们只会被代码中的错误触发。一旦代码经过测试和交付,就不再需要它们,应该关闭它们以使代码运行得更快。断言是一种调试工具。这里有两个明显错误的假设。首先,他们假设测试发现了所有的错误。实际上,对于任何复杂的程序,您都不太可能测试您的代码将通过的排列的一小部分(请参阅无情测试)。其次,乐观主义者忘记了你的程序运行在一个危险的世界中。在测试期间,老鼠可能不会啃通讯线,玩游戏的人不会耗尽内存,日志文件也不会填满硬盘。当您的程序在生产环境中运行时,这些事情可能会发生。您的第一道防线是检查任何可能的错误,第二道防线是使用断言来尝试检测您遗漏的那些。在将程序交付生产时关闭断言就像在没有网络的情况下穿越高线,因为您曾经在实践中穿越过。有巨大的价值,但很难获得人寿保险。即使你确实有性能问题,也只关闭那些真正打击你的断言。


T
Thomas Danecker

您应该始终使用第二种方法(抛出异常)。

此外,如果您在生产中(并且有发布版本),最好抛出异常(并让应用程序在最坏的情况下崩溃)而不是使用无效值并可能破坏您客户的数据(这可能会花费数千美元)。


不,与此处的其他一些答案相同:您并不真正了解其中的区别,因此您选择退出其中一个产品,在此过程中在它们之间建立了错误的二分法。矮
这是 IMO 列表中唯一正确的答案。卡斯帕,不要这么轻易地忽略它。调试断言是一种反模式。如果它在调试时不变,则它在运行时不变。允许您的应用程序继续使用损坏的不变量会使您处于不确定状态,并且可能会出现比崩溃更严重的问题。 IMO 最好在两个构建中使用相同的代码,这些代码会因合同损坏而快速失败,然后在顶层实施强大的错误处理。例如,隔离组件并实现重新启动它们的能力(就像浏览器中的选项卡崩溃不会导致整个浏览器崩溃)。
在此处的讨论中包含 Trace.Assert 可能会有所帮助,因为它不能被相同的论点驳回。
o
orlando calresian

您应该使用 Debug.Assert 来测试程序中的逻辑错误。编译器只能通知您语法错误。所以你应该明确地使用 Assert 语句来测试逻辑错误。比如说测试一个销售汽车的程序,只有蓝色的宝马才能获得 15% 的折扣。编译器无法告诉您您的程序在执行此操作时是否在逻辑上正确,但断言语句可以。


对不起,但例外做所有相同的事情,所以这个答案并没有解决真正的问题。
A
AlexDev

我在这里阅读了答案,我认为我应该添加一个重要的区别。有两种非常不同的使用断言的方式。一个是作为“这不应该真的发生,所以如果它确实让我知道所以我可以决定做什么”的临时开发人员快捷方式,有点像条件断点,用于您的程序能够继续的情况。另一种方法是在代码中对有效程序状态进行假设。

在第一种情况下,断言甚至不需要在最终代码中。您应该在开发过程中使用 Debug.Assert,并且可以在不再需要时将其删除。如果您想留下它们或忘记删除它们也没问题,因为它们不会在发布编译中产生任何影响。

但在第二种情况下,断言是代码的一部分。他们,好吧,断言,你的假设是真实的,并且记录它们。在这种情况下,您真的想将它们留在代码中。如果程序处于无效状态,则不应允许其继续。如果您无法承受性能损失,您就不会使用 C#。一方面,如果发生这种情况,能够附加调试器可能会很有用。另一方面,您不希望堆栈跟踪出现在您的用户身上,也许更重要的是您不希望他们能够忽略它。此外,如果它在服务中,它将始终被忽略。因此,在生产中,正确的行为是抛出异常,并使用程序的正常异常处理,这可能会向用户显示一条好消息并记录详细信息。

Trace.Assert 有完美的方法来实现这一点。它不会在生产中被删除,并且可以使用 app.config 配置不同的侦听器。因此,对于开发,默认处理程序很好,对于生产,您可以创建一个简单的 TraceListener,如下所示,它会引发异常并在生产配置文件中激活它。

using System.Diagnostics;

public class ExceptionTraceListener : DefaultTraceListener
{
    [DebuggerStepThrough]
    public override void Fail(string message, string detailMessage)
    {
        throw new AssertException(message);
    }
}

public class AssertException : Exception
{
    public AssertException(string message) : base(message) { }
}

在生产配置文件中:

<system.diagnostics>
  <trace>
    <listeners>
      <remove name="Default"/>
      <add name="ExceptionListener" type="Namespace.ExceptionTraceListener,AssemblyName"/>
    </listeners>
  </trace>
 </system.diagnostics>

u
unexist

我不知道它在 C# 和 .NET 中的情况如何,但在 C 中 assert() 仅在使用 -DDEBUG 编译时才有效 - 如果没有使用 -DDEBUG 编译,最终用户将永远不会看到 assert()。它仅供开发人员使用。我经常使用它,有时更容易跟踪错误。


m
mattlant

我不会在生产代码中使用它们。抛出异常,捕获并记录。

在 asp.net 中也需要小心,因为断言可能会出现在控制台上并冻结请求。