写点什么

C#的未来:方法契约

  • 2015-05-20
  • 本文字数:2618 字

    阅读完需:约 9 分钟

近些年来,开发者可以通过代码契约(Code Contracts)这个研究性项目获得添加方法级别契约的能力,但这种方式存在许多问题,它所使用的命令式语法相当冗长,并且通过工具提供的语法支持也很差。无论是开发类库或是应用程序,要完整的利用这一契约特性,必须要运行某种编译后指令。总的来说,这是一个有趣的项目,但要真正变得实用,还需要第一等的编译器与语法的支持。

第119 号提议——方法契约旨在提供这种支持。这一语法要求在方法签名与方法体之间定义前置与后置条件,与泛型的约束写法类似。下面这个示例展示了该语法的表现形式:

public int Insert(T item, int index)
requires index >= 0 && index <= Count
ensures return >= 0 && return < Count
{ … }

这条提议中共包含三个新的关键字。“requires”开头的语句负责处理前置条件,多数情况下将用于检查参数,但理论上也可以用于检查对象本身的状态。“ensures”开头的语句用于设定后置条件,它重用了“return”关键字,以指代该方法调用的返回结果。

快速失败还是抛出异常

类似于代码契约,这条提议最初的目的也是产生快速失败。这是一种相当激进的强制契约形式,任何对契约的违反都会立即导致应用的崩溃。在这种模型下,倾向于抛出异常的开发者不得不手动地进行标注:

public int Insert(T item, int index)
requires index >= 0 && index <= Count
else throw new ArgumentOutOfRangeException(nameof(index))
ensures return >= 0 && return < Count
{ … }

对于该提议的这一部分,人们的反对相当激烈。

Nathan Jervis 写道:

你了解程序中哪些部分不会受到影响,并且能够安全地继续执行下去。可能在某些情况下你会编写某些极其关键的代码,或许你会希望实现快速失败,但我不认为了解你的程序中哪一部分产生错误是一件不可能的事。

以立即干掉整个进程的方式作为正确的处理行为,这种假设让人觉得十分可笑。如果微软的 Word 有某个代码错误,在将文件保存到某个网络路径时产生了一个 bug,你会希望它立即干掉整个应用吗?不,你会希望它能够将文件暂存在某个临时路径下,然后为用户显示一条错误信息,记录这个问题,最后在下次加载文件时让用户选择尝试恢复它。

HaloFour 也表示附和:

我认为由于参数校验违反契约而导致整个进程崩溃绝对是一种愚蠢的做法。这种实现方式肯定会导致这一特性将无人问津,除非某人有意要写一些难懂的代码。这一特性中的这方面目的在于参数校验,而程序完全能够以某种形式从参数校验错误的情况下恢复正常运行。而且坦白地说,即使程序无法恢复正常,调用者也可以决定不要捕获这个异常。这也是我所看到在.NET 或其它任何语言中实现的代码契约的工作方式。

David Nelson 则提到了代码契约的经历:

如果你之前曾经使用过代码契约进行开发,那么你一定注意到它在是否应当采取快速失败这一方式上曾经导致大量的争论。代码契约团队进行了几个月(甚至几年?)的努力,试图说服整个社区:快速失败才是正确的做法,可最终他们还是失败了。我深切感受到了那个极具误解性的决定所带来的后果,这让我无法拥抱这一提议。我曾经是快速失败这一荒谬的方式最坚定的反对者,而且我还会继续反对下去。

稍后,他明确地列举了快速失败方式所导致的问题:

1) 你怎样记录错误日志?使用 Watson 显然不能满足需求,绝大多数的.NET 应用程序都不会使用它,因为它提供的信息非常有限并且难以理解,访问这些信息也很困难。我所看到过的每个.NET 应用程序都会生成独有的错误日志。

2) 只因为某个用户在某种极端情况下遇到了一个无害的逻辑 bug,就要让为全球几百万用户提供服务的某个生产环境中的 web 服务器挂掉,这种做法真的合适吗?

3) 如果某个单元测试违反了契约的话该怎么办?让单元测试执行器崩溃吗?

4) 如果一个程序的错误会导致进程的崩溃,为什么在.NET 中其它的错误情况下会抛出异常?NullReferenceException 又为什么还会存在,难道不应当直接干掉进程吗?为什么在 JIT 过程中编译某个方法失败时(这很明显意味着存在某个比违反契约严重得多的问题)会抛出异常,而不是直接干掉进程?

Aaron Dandy 则希望能够得到两种选择:

我当然会使用快速失败,但我只想在我私人的工作中使用它,在公共项目中我还是希望使用异常。如果调用我代码的用户决定用大量的异常去喂饱异常这个怪兽(即选择使用异常),那也是他们自己的选择,他们(同时也隐含了他们代码的用户)也需要为这一决定买单。

HaloFour 对以下观点表示同意:

我更希望方法契约能够抛出异常(至少是对于 requires 语句来说),然后添加一个语言关键字断言,在某种条件未满足的情况下会快速失败。

异常类型

这条提议中比较容易接受的部分是 Argument 异常,编译器会将某个简单的 requires 语句转化为某个 ArgumentNullException 或 ArgumentOutOfRange 异常。如果 requires 语句检查的内容是对象的状态,那么它可以抛出一个 InvalidOperationException 异常。但如果该语句同时检查参数与对象状态呢?这种情况下要决定抛出何种异常会成为一个相当复杂的问题。

另外一个问题在于 ObjectDisposedException,因为没有什么标准方式能够表现一个被回收的对象。因此只能采取一些宽松的约定,检查是否存在某个叫做 _disposed 或 m_IsDisposed 之类名称的布尔型字段。这一点的重要性在于,InvalidOperationException 异常一般来说能够通过改变对象状态的方式进行恢复,而 ObjectDisposedException 永远做不到这一点。

另一方面,需要通过某种异常表示 ensures 语句出错。与 requires 语句不同,在 ensures 契约中的错误总是意味着在方法内部存在 bug。

本地化

假设我们采取了某种基于异常的方式,那么接下来的问题就是本地化了。对于基本的参数检查来说,编译器可以简单地生成包含英文文本的参数异常。但如果这些异常信息需要本地化为其它语言呢?如果选择使用简化的语法,就不会明确地抛出某个参数异常。这种情况下,或者需要通过某种渠道添加本地化的信息,或者不得不使用冗长的语法以显示本地化异常信息。

枚举与契约

目前为止所讨论的契约都是一种附加条件,而在 Fabian Schmied 所提出的提议中,编译器将允许省略那些绝对不会命中的 return 语句。

public string GetText (MyEnum myEnum)
requires defined(myEnum)
{
switch (myEnum)
{
case One: return “Single”;
case Two: return “Pair”;
case Three: return “Triple”;
}
// 所有分支情况都已涵盖,因此即使省略了 return 语句也不会产生错误。
}

查看英文原文: C# Futures: Method Contracts

2015-05-20 08:542585
用户头像

发布了 428 篇内容, 共 176.7 次阅读, 收获喜欢 38 次。

关注

评论

发布
暂无评论
发现更多内容

【文末已开奖】10月征文:说出你和极客时间的故事

InfoQ写作社区官方

征文活动 热门活动 活动预告 我和极客时间的故事

Beyond the Limits: IPQ9574 and QCN6274 - the pinnacle of network performance

wifi6-yiyi

qcn6274 ipq9574

千帆大模型平台中的Prompt:开发效率与创新的助推器

Geek_cf26da

大模型训练 文心千帆 千帆大模型平台

dapp合约质押挖矿开发稳定版丨dapp合约挖矿系统开发(项目方案)

V\TG【ch3nguang】

DAPP质押挖矿理财系统开发技术搭建

V\TG【ch3nguang】

什么???CSS也能原子化! | 京东云技术团队

京东科技开发者

CSS 原子化服务 企业号10月PK榜 unocss

华大北斗荣获2022年广东省制造业单项冠军

江湖老铁

log4j2同步日志引发的性能问题 | 京东物流技术团队

京东科技开发者

log4j2 Log4j2 漏洞 日志同步 企业号10月PK榜 性能问题

RAG (检索增强生成)技术详解:揭秘基于垂直领域专有数据的Chatbots是如何实现的

Baihai IDP

AI 白海科技 rag 检索增强生成 Chatbots

DAPP智能合约借贷质押挖矿系统开发详情分析

V\TG【ch3nguang】

为什么SFA系统会流于形式,赋能型CRM能帮企业解决哪些问题

用友BIP

数智营销

电商运营该如何做AB测试

字节跳动数据平台

大数据 电商 AB testing实战 A/B 测试 企业号9月PK榜

第4期 | 锐变 海量数据、全量洞察

用友BIP

项目管理

DeFi/DAPP质押挖矿系统技术开发

V\TG【ch3nguang】

主要的商业云管平台公司有哪些?大家推荐哪家?

行云管家

云计算 企业上云 云管平台 云管理

简单好用的磁盘管理工具 DiskCatalogMaker 中文版

胖墩儿不胖y

磁盘管理 Mac软件 磁盘管理工具

垂直大模型训练的关键步骤与策略

Geek_cf26da

大模型训练 千帆大模型平台

大模型训练对底模型的影响及应对策略

Geek_cf26da

大模型 文心千帆 千帆大模型平台

秋招过半零Offer怎么办?

王磊

Java

Chiplet解决芯片技术发展瓶颈

IC男奋斗史

封装 芯片 半导体 chiplet

jq工具及其常用用法 | 京东物流技术团队

京东科技开发者

json 数据处理 jq 企业号10月PK榜

【京东开源项目】微前端框架MicroApp 1.0正式发布

京东科技开发者

开源 微前端 微前端框架 企业号10月PK榜 MicroApp

大模型训练:深度学习的高级挑战

Geek_cf26da

大模型训练 大模型 千帆大模型平台

Fine-tuning: 高效微调大模型的策略

Geek_cf26da

大模型训练 文心千帆 千帆大模型平台

教你如何基于MindSpore进行ChatGLM微调

华为云开发者联盟

人工智能 模型 华为云 华为云开发者联盟

华为云发布CodeArts APIMock服务,精准Mock,并行开发零等待!

华为云开发者联盟

云计算 华为云 华为云开发者联盟 华为云CodeArts 并行开发

C#的未来:方法契约_C#_Jonathan Allen_InfoQ精选文章