Visual Studio 2010 中的代码“.NET研究”约定设置

简介:   软件约定称为代码约定,通过这一约定可以表示代码正常工作所需的正式条件。 如果方法未按预期收到数据或生成的数据不符合预期的后置条件,代码约定将导致代码引发异常。 有关前置条件和后置条件的概述,您可能需要查看我上个月发表的文章 (msdn.microsoft.com/magazine/gg983479)。

  软件约定称为代码约定,通过这一约定可以表示代码正常工作所需的正式条件。 如果方法未按预期收到数据或生成的数据不符合预期的后置条件,代码约定将导致代码引发异常。 有关前置条件和后置条件的概述,您可能需要查看我上个月发表的文章 (msdn.microsoft.com/magazine/gg983479)。

  代码约定是 .NET Framework 4 的一部分,但同样依赖于 Visual Studio 2010 中的一些功能,例如运行时工具、与 MSBuild 集成以及“项目属性”框中的属性页。 值得注意的是,仅编写前置条件和后置条件是不够的。 您还需要为每个项目启用运行时检查功能才能使用软件约定。 您可以通过 Visual Studio 2010 中的“代码约定”项目属性页来完成上述操作。

  在本文中,我将讨论您可以查看或选择的各个选项的预定用途,并深入讨论使用代码约定中的参数验证可以执行的最常见操作的重写程序工具和实践。

  代码约定属性页

  应在所有版本中还是仅在调试版本中实施代码约定前置条件和后置条件? 实际上,这取决于您对软件约定概念的理解。 它是设计工作的一部分吗? 或者,它仅是一种调试措施?

  如果它是设计功能,则没理由剥离发行版中的约定。 如果它仅是一种调试技术,当在发布模式中进行编译时,您不希望显示它。

  在 .NET Framework 中,代码约定仅是此框架的一部分并且未融入任何语言。 这样将更容易在项目中按版本配置它们。 因此,通过软件约定的 .NET Framework 实现,您可以决定实现约定的合适时间和地点。

  图 1 显示 Visual Studio 2010 中的属性页,通过此页可以设置软件约定为应用程序工作的方式。 请注意,此类设置基于项目应用,因此可以根据需要进行调整。

图 1 Visual Studio 2010 中代码约定的属性页

  您可以选择选项配置(调试、发布等)并仅对该配置应用设置。 这样,您可以启用代码约定用于调试但不用于发布,而且更重要的是,您可以随时改变决策。

  运行时检查

  若要启用代码约定,必须选中“执行运行时约定检查”选项。 如果未选中此选项,则在源代码中显示的任何约定说明将可能不会产生任何效果(定义了 DEBUG 符号的任何版本中的 Contract.Assert 和 Contract.Assume 例外,但这不是很重要)。 复选框控制是否在每个编译步骤结束时触发重写程序工具。 重写程序是一个外部工具,用于对软件约定进行后处理并修改 MSIL 代码,以及在合适的位置执行前置条件、后置条件和固定条件检查。

  但是,请注意,如果您具有类似下面这样的前置条件,则在关闭重写程序时会得到运行时断言失败:

 
 
Contract.Requires < TException > (condition)

  图 2 显示了您得到的消息框。

图 2 代码需要运行时约定检查

上海企业网站制作>

  若要详细查看运行时检查的工作方式,请考虑以下代码:

 
 
public Int32 Sum(Int32 x, Int32 y) {
// Check input values
ValidateOperands(x, y);
ValidateResult();

// Perform the operation
if (x == y)
return 2 * x;
return x + y;
}
  

  约定详细信息使用 ContractAbbreviator 存储在 ValidateXxx 方法中,如上个月的专栏所讨论。 以下是 ValidateXxx 方法的源代码:

 
 
[ContractAbbreviator]
private void ValidateOperands(Int32 x, Int32 y) {
Contract.Requires(x
>= 0 && y >= 0 );
}

[ContractA上海徐汇企业网站设计与制作bbreviator]
private void ValidateResult() {
Contract.Ensures(Contract.Result
< Int32 > () >= 0 );
}
  

  如果您使用 Contract.Requires 而不是 Contract.Requires<TException>,则在某个版本中关闭重写程序时不会出现图 2 所示的失败。 图 2 中的消息框是由 Contract.Requires 的内部实现所致,如下所示:

 
 
[Conditional( " CONTRACTS_FULL " )]
public static void Requires( bool condition, string userMessage) {
AssertMustUseRewriter(
ContractFailureKind.Precondition,
" Requires " );
}

public static void Requires < TException > ( bool condition)
where TException: Exception {
AssertMustUseRewriter(
ContractFailureKind.Precondition,
" Requires<TException> " );
}
       
上海徐汇企业网站制作pre>

  条件属性向编译器指示除非定义了 CONTRACTS_FULL 符号,否则应忽略此类方法调用。 仅当您启用“执行运行时约定检查”选项时,才定义此符号。 由于 Contract.Requires<TException> 不是根据条件定义的且缺少该属性,因此将执行重写程序检查,如果禁用运行时约定检查,则会导致失败的断言。

  接下来我们将考虑对上述代码使用重写程序的效果。 您可以方便地亲自验证我所说的,方法是仅使用断点并按 Ctrl+F11 在 Visual Studio 2010 中打开反汇编视图。 图 3 显示了在未启用运行时约定检查的情况下,逐步查看编译的 Sum 方法时反汇编视图的内容。 正如您所看到的,源代码与您在类中编写的代码相同。

图 3 不执行运行时约定检查时的反汇编视图

  如果启用运行时检查,重写程序工具将通过编译器传递,返回并编辑 MSIL 代码。 如果您在启用代码约定的情况下逐步执行相同代码,将看到类似图 4 的内容。

图 4 Return 语句后执行的后置条件检查

  明显的区别是在退出 Sum 方法之前且在 return 语句之后调用 ValidateResult。 您不必是 MSIL 专家就能了解图 4 中所示代码的状况。 在方法开始接受最上面位置的前置条件之前,将对操作数进行验证。 包含后置条件的代码将移动到方法的底部,最后一个 return 语句的 MSIL 代码将也是如此。 更有意思的是,第一个 return 语句(Sum 方法中实现快捷方式的语句)现在只跳到 ValidateResult 开始的地址:

 
 
...
return
2 * x ;
00000054 mov eax , dword ptr [ ebp-8 ]
00000057 add eax , eax
00000059 mov dword ptr [ ebp-0Ch ] , eax
0000005c nop
0000005d jmp 0000006B
...
ValidateResult()
;
0000006b push dword ptr ds: [ 02C32098h ]
...

  回到图 1,请注意“执行运行时约定检查”复选框旁边的下拉列表。 您可以通过该列表指示要启用的软件约定数目。 存在多个级别:“完全”、“前置和后置”、“前置条件”、“发行版要求”和“无”。

  “完全”表示支持所有类型的软件约定,“无”表示不考虑任何软件约定。 “前置和后置”排除固定条件。 “前置条件”还排除 Ensure 语句。

  “发行版要求”不支持 Contract.Requires 方法,仅允许使用 Requires<TException> 或旧的 If-Then-Throw 格式指定前置条件。

  通过项目属性页可按项目启用或禁用运行时检查,但是如果您只想对代码的一些部分禁用运行时检查该怎么办? 在这种情况下,只需使用 ContractRuntimeIgnored 属性以编程方式禁用运行时检查。 但是,最新发行版 (1.4.40307.0) 中增加了新的“跳过限定符”选项,该选项也不允许您执行任何包含对 Contract.ForAll 或 Contract.Exists 的引用的约定。

  可以对在 Contract 表达式中使用的成员应用属性。 如果成员已使用此属性加以修饰,则显示该成员的整个 Contract 语句将不会进行运行时检查。 属性不会在 Assert 和 Assume 等 Contract 方法中识别。

  程序集模式

上海企业网站设计与制作

  代码约定属性还可用于为约定配置“程序集模式”设置。 此设置是指您打算执行参数验证的方式。 有两个可能的选项:“标准约定要求”和“约定引用程序集”。 程序集模式设置可帮助重写程序等工具在必要时优化代码并给出合适的警告。 假设您使用程序集模式来声明您使用代码约定进行参数验证的意图。 程序集模式设置引入了一些必须符合的简单规则,否则您将收到编译错误。

  如果您使用 Requires 和 Requires<T> 方法验证参数,程序集模式必须设置为“标准约定要求”。 如果您使用任何 If-Then-Throw 语句作为前置条件,则应使用“自定义参数验证”。 如果您不使用“自定义参数验证”,该语句将被视为 Requires<T>。 自定义参数验证的组合以及任何形式的 Requires 语句的显式使用将引发编译器错误。

  使用 Requires 和使用 If-Then-Throw 语句之间有何差别? If-Then-Throw 语句在验证失败时始终引发您指示的异常。 在这一点上,它与 Requires 不同,但与 Requires<T> 相似。 纯 If-Then-Throw 语句也不会被约定工具(重写程序和静态检查程序)发现,除非您在该语句后调用 EndContractBlock。 使用 EndContractBlock 时,它必须是您在方法中调用的最后一个代码约定方法。 其后不能执行任何其他代码约定调用:

 
 
if (y == 0 )
throw new ArgumentException();
Co上海闵行企业网站制作ntract.EndContractBlock();
 

  此外,Requires 语句是自动继承的。 除非您也使用 EndContractBlock,否则不会继承 If-Then-Throw 语句。 在旧模式中,不会继承 If-Then-Throw 约定。 实际上,您必须手动执行约定继承。 如果这些工具未检测到前置条件在重写和接口实现中重复,将尝试发出警告。

  最后,请注意,不允许 ContractAbbreviator 包含任何 If-Then-Throw 语句,但您可以对该属性使用约定验证程序。 缩写方法只能包含常规 Contract 语句进行参数验证。

  其他设置

  在代码约定属性页中,如果选中“执行运行时约定检查”选项,则将启用其他一些有用选项。

  如果启用“约定失败时断言”选项,则当违反约定时,将导致描述失败上下文的断言。 您将看到类似于图 2 中所示内容的消息框,并且可以选择一些选项。 例如,您可以再次尝试附加调试器,中止应用程序或者直接忽略失败并继续。上海闵行企业网站设计与制作

  您可能希望仅将此选项用于调试版本,因为显示的信息对于一般最终用户来说可能没有意义。 代码约定 API 提供了一个集中式异常处理程序用来捕获任何冲突,并由您判断错误的真正根源。 您收到的信息将区分是前置条件、后置条件还是固定条件失败,但仅使用布尔表达式并可能使用配置的错误消息来描述错误特征。 换句话说,从集中式异常处理程序轻松恢复有点难度:

 
 
Contract.ContractFailed += CentralizedErrorHandler;

  下面是说明处理程序的一些代码:

 
 
static void CentralizedErrorHandler(
Object sender, ContractFailedEventArgs e) {
Console.WriteLine(
" {0}: {1}; {2} " , e.
FailureKind, e.Condition, e.Message);
e.SetHandled();
}
       

  如果要在运行时引发特定异常,则可以使用 Requires<TException>。 如果您打算限制调试版本约定的使用或者如果您不关心异常的实际类型是什么,则可以使用 Requires 和集中式处理程序。 通常这足够指明发生了错误。 例如,许多应用程序在顶层都具有可捕获各种类型异常并指出如何重新启动的全能功能。

  “仅公共接口约定”选项是指您希望实施代码约定的位置:每个方法或仅公共方法。 如果选中该选项,重写程序将忽略代码约定语句的私有和受保护成员,并仅处理公共成员的约定。

  如果您将代码约定融入您的整体设计从而在任何地方使用代码约定,选中此选项很有意义。 但是,一旦应用程序做好交付准备,作为一种优化形式,您可以不必检查内部成员参数,因为没有外部代码直接调用这些成员。

  将代码约定限制为程序集的公共接口的想法是否好用,还取决于您编写代码的方式。 如果您可以保证公共接口对较低级别发出的任何调用都是正确的,则这是一种优化形式。 如果不能保证,禁用内部方法的约定可导致出现令人厌烦的错误。

  “调用网站需要检查”选项提供了另一种优化方案。 假设您正在编写要由其他程序集中的模块使用的库。 出于性能考虑,您禁止对约定进行运行时检查。 但是,只要您创建了约定引用程序集,就可以使调用方检查所调用的每个方法的约定。

  包含使用代码约定的类的每个程序集中都可能存在约定引用程序集。 其中包含具有 Contract 语句但没有其他代码的父程序集的公共可见接口。 可以从代码约定属性页对程序集的创建进行排序和控制。

  旨在调用库的任何代码都可能引用约定引用程序集,并且可以通过仅启用“调用网站需要检查”选项自动导入约定。 处理调用方代码时,重写程序将为随约定引用程序集一起提供的外部程序集上调用的每个方法导入约定。 这种情况下,将在调用站点(位于调用方侧)检查约定,并保留可对不直接控制的代码启用和禁用的可选行为。

  总结

  代码约定是 .NET Framework 的一个方面,值得进行更多研究。 我这里只对配置选项进行了简要的讨论,甚至未涉及静态检查程序的使用。 代码约定可帮助您更清楚地设计应用程序以及编写更简洁的代码。

  若要了解有关代码约定的详细信息,请参见 Melitta Andersen 2009 年 6 月的“CLR 全面透彻解析”专栏 (msdn.microsoft.com/magazine/ee236408) 和 DevLabs Code Contracts 网站 (msdn.microsoft.com/devlabs/dd491992)。 您还可以在 Microsoft Research 的代码约定网站 (research.microsoft.com/projects/contracts) 上找到有关代码约定开发的有趣背景信息。

目录
相关文章
|
9天前
Visual Studio 快速分析 .NET Dump 文件
【11月更文挑战第10天】.NET Dump 文件是在 .NET 应用程序崩溃或出现问题时生成的,记录了应用程序的状态,包括内存对象、线程栈和模块信息。通过分析这些文件,开发人员可以定位和解决内存泄漏、死锁等问题。在 Visual Studio 中,可以通过调试工具、内存分析工具和符号加载等功能来详细分析 Dump 文件。此外,还可以使用第三方工具如 WinDbg 进行更深入的分析。
|
30天前
|
安全 Java 网络安全
Android远程连接和登录FTPS服务代码(commons.net库)
Android远程连接和登录FTPS服务代码(commons.net库)
24 1
|
1月前
|
前端开发 JavaScript C#
CodeMaid:一款基于.NET开发的Visual Studio代码简化和整理实用插件
CodeMaid:一款基于.NET开发的Visual Studio代码简化和整理实用插件
|
3月前
|
Kubernetes 监控 Devops
【独家揭秘】.NET项目中的DevOps实践:从代码提交到生产部署,你不知道的那些事!
【8月更文挑战第28天】.NET 项目中的 DevOps 实践贯穿代码提交到生产部署全流程,涵盖健壮的源代码管理、GitFlow 工作流、持续集成与部署、容器化及监控日志记录。通过 Git、CI/CD 工具、Kubernetes 及日志框架的最佳实践应用,显著提升软件开发效率与质量。本文通过具体示例,助力开发者构建高效可靠的 DevOps 流程,确保项目成功交付。
76 0
|
3月前
|
XML 开发框架 .NET
.NET框架:软件开发领域的瑞士军刀,如何让初学者变身代码艺术家——从基础架构到独特优势,一篇不可错过的深度解读。
【8月更文挑战第28天】.NET框架是由微软推出的统一开发平台,支持多种编程语言,简化应用程序的开发与部署。其核心组件包括公共语言运行库(CLR)和类库(FCL)。CLR负责内存管理、线程管理和异常处理等任务,确保代码稳定运行;FCL则提供了丰富的类和接口,涵盖网络、数据访问、安全性等多个领域,提高开发效率。此外,.NET框架还支持跨语言互操作,允许开发者使用C#、VB.NET等语言编写代码并无缝集成。这一框架凭借其强大的功能和广泛的社区支持,已成为软件开发领域的重要工具,适合初学者深入学习以奠定职业生涯基础。
102 1
|
3月前
|
微服务 API Java
微服务架构大揭秘!Play Framework如何助力构建松耦合系统?一场技术革命即将上演!
【8月更文挑战第31天】互联网技术飞速发展,微服务架构成为企业级应用主流。微服务将单一应用拆分成多个小服务,通过轻量级通信机制交互。高性能Java Web框架Play Framework具备轻量级、易扩展特性,适合构建微服务。本文探讨使用Play Framework构建松耦合微服务系统的方法。Play采用响应式编程模型,支持模块化开发,提供丰富生态系统,便于快速构建功能完善的微服务。
49 0
|
3月前
|
SQL 开发框架 .NET
代码更简洁,开发更高效:从零开始使用Entity Framework Core与传统ADO.NET构建数据持久化层的比较
【8月更文挑战第31天】在.NET平台上开发数据驱动应用时,选择合适的ORM框架至关重要。本文通过对比传统的ADO.NET和现代的Entity Framework Core (EF Core),展示了如何从零开始构建数据持久化层。ADO.NET虽强大灵活,但需要大量手写代码;EF Core则简化了数据访问,支持LINQ查询,自动生成SQL命令,提升开发效率。从创建.NET Core项目、定义数据模型、配置`DbContext`到执行数据库操作,EF Core提供了一套流畅的API,使数据持久化层的构建变得简单直接。
39 0
|
3月前
|
存储 Linux 网络安全
【Azure App Service】.NET代码实验App Service应用中获取TLS/SSL 证书 (App Service Linux/Linux Container)
【Azure App Service】.NET代码实验App Service应用中获取TLS/SSL 证书 (App Service Linux/Linux Container)
|
3月前
|
网络安全 API 数据安全/隐私保护
【Azure App Service】.NET代码实验App Service应用中获取TLS/SSL 证书 (App Service Windows)
【Azure App Service】.NET代码实验App Service应用中获取TLS/SSL 证书 (App Service Windows)
|
6月前
Visual Studio 2022 中VLD库如何安装
Visual Studio 2022 中VLD库如何安装
732 1
下一篇
无影云桌面