“Contract.Requires”在没有 ccrewrite 的情况下如何表现?这与“要求”有何不同
本文关键字:何表现 情况下 这与 何不同 要求 ccrewrite Requires Contract | 更新日期: 2023-09-27 18:36:41
不使用ccrewrite时(假设项目是由另一个未安装CC的开发人员构建的),
Contract.Requires<T>(cond)
是静默剥离还是仍然导致等同于if (!cond) { throw new T() }
的行为?(我不在乎它是另一个还是两个方法调用 - 但它应该"始终被检查"。
我问是因为Contract.Requires<T>
的行为似乎与Contract.Requires
不同,但我不确定"如何"或"何时"。
目标是取代公共合同的结构
if (x != null) throw new ArgumentNullException();
使用 CC 兼容版本,在构建步骤中未执行 CC 重写时仍会引发异常。
虽然上述带有EndContractBlock
确实适用于"自定义参数验证"(即旧合约模式),但我想在项目中使用"标准合约要求"。
我相信可能存在等效性,因为在"自定义参数验证"模式下,我无法使用Requires<T>
;如果没有等效性到始终需要的检查,那么了解为什么会很好。
当 CC 重写未完成时,我可以丢失Requires
、Ensures
和 保留非兑现不变合约方法和接口合约,因为我重视它们进行静态分析 - 但我需要这些始终存在的边界检查来论证保留 CC。
请参阅代码契约手册。它肯定会告诉您所有您需要了解的有关各种形式的代码协定检查如何工作以及使用每种表单时需要设置的选项的所有信息。
Contract.Requires(bool cond)
和Contract.Requires<TException>(bool cond)
有什么区别?
要回答您的第一个问题,请参阅手册中的第 2.1 节前提条件。简而言之,这是区别:
Contract.Requires(bool cond)
如果条件的计算结果为 false
,这将引发私有Contract.ContractException
异常。您无法捕获此异常(因为从您的角度来看它是私有的)——这是为了阻止捕获和处理它,从而使合同几乎毫无价值。
Contract.Requires<TException>(bool cond)
如果条件的计算结果为 false
,则将抛出指定的TException
。如果不在所有构建上运行合约工具,则无法使用此窗体。
关于ccrewrite
具体而言,在第 20 页的第 5 节中。使用指南,它告诉您代码协定可以使用的所有不同形式的协定,它们是如何工作的,以及每种协定的构建要求是什么。
我将简要总结一下,但请下载手册并阅读。它非常好,尽管在任何方面都不完整 - 您必须进行大量实验才能学习如何有效地使用代码契约。此外,如果您可以访问PluralSight,John Sonmez有一门名为Code Contracts的课程,这是一门很棒的入门课程;迈克尔·佩里(Michael Perry)有一门很棒的课程,叫做可证明代码。
已发布的代码不需要合约检查
如果不需要在已发布的代码中进行合约签入,则:
- 随时随地使用
Contract.Requires
- 仅在调试版本上启用运行时检查。
已发布的代码需要进行代码协定检查
如果需要对已发布的代码进行合约检查,则有两种选择:
- 使用代码协定"本机"前提条件:
- 在要为其抛出特定异常的公共 API 方法(例如,将由库用户调用的方法)上使用
Contract.Requires<TException>
,例如ArgumentException
. - 使用
Contract.Requires
对于非公共 API 方法或不希望引发特定异常的公共 API 方法。 - 对所有生成启用运行时检查。
- 确保启用选项以仅发出前置条件,并且仅在程序集的公共外围设备上发出,例如,仅那些可由库使用者调用的方法。
- 在要为其抛出特定异常的公共 API 方法(例如,将由库用户调用的方法)上使用
- 使用"旧版"合同检查:
- 这是您的旧派风格
if (cond) { throw new Exception(...) }
在您的公共 API 方法上保护块 - 使用手动继承对派生类型强制实施协定。(使用选项 1 时,代码协定可以从基类自动继承协定,从而帮助您避免违反 Liskov 替换原则。
- 确保将行放在所有
if (cond) { throw new Exception(...) }
块之后Contracts.EndContractBlock()
,以便代码协定知道这些是你的协定。 - 在非公共 API 方法上,您可以随意将
Contract.Requires
用于您的合约。 - 仅在调试版本上启用运行时检查。
- 这是您的旧派风格
关于上述需要注意的一件事:在调试版本上始终启用合同检查。如果团队中的其他开发人员将构建此库,则他们还需要安装代码协定。
从 Section 5.1.3: 强制项目使用合约构建:
如果您使用的是方案 2 (Requires〈Exn〉),并且将源代码提供给其他开发人员,则可能需要提醒他们他们需要使用这些工具来构建源代码。如果是这样,您可以在最后(在导入 CSharp 或 VisualBasic 目标之后)将以下代码片段插入到项目文件中:
<PropertyGroup> <CompileDependsOn>$(CompileDependsOn);CheckForCodeContracts</CompileDependsOn> </PropertyGroup> <Target Name="CheckForCodeContracts" Condition="'$(CodeContractsImported)' != 'true'"> <Error Text="Project requires Code Contracts: http://msdn.microsoft.com/en-us/devlabs/dd491992.aspx" /> </Target>
另请参阅第 6.1 节:组装模式,其中告诉您自定义参数验证和标准协定要求之间的区别。本节非常清楚地表明,合约重写器(ccrewrite
)总是在调试版本上运行。