使用TFS检测.NET代码中的破坏性更改?

12

我希望在TFS构建解决方案时,能够检测.NET代码(特别是C#)中的破坏性变更。如果在检入的代码与最近成功构建版本之间存在任何破坏性变更(例如 "A definite guide to API-breaking changes in .NET" 中所述),我想知道它们的情况。破坏性变更不一定会导致构建失败。除了编写一个使用反射来比较相同程序集的两个版本的应用程序,还有什么其他方法可以完成这项工作?


链接的问题:https://dev59.com/SXE95IYBdhLWcg3wSb7P - linuxbuild
4个回答

6
为了更详细地阐述James和Adam的答案,我想提供有关使用NDepend及其代码查询和规则功能检测破坏性变化的详细信息。免责声明:我是该工具的开发人员之一。
NDepend已经发展并改进了其查询语言。如果您下载NDepend试用版并分析您要搜索破坏性变化的两个版本的代码库,请查看默认代码规则组API Breaking Changes中以下CQLinq规则的链接 执行这些代码规则看起来像这样,例如(NUnit v2.5.8和v2.5.3之间的差异):

API breaking changes


5
是的,我会(也确实使用)NDepend来做这件事。 我在一个为开发人员提供可扩展 API 的产品上工作。因此,我们需要确保在发布版本之间,不会删除那些开发人员可能依赖的功能。 另一方面,我们需要灵活性来发展产品,而不会有关于回退版本的大量限制。
一些你需要考虑的事情:
  1. 更改所引用 DLL 的版本应该被视为破坏性更改。
  2. 删除/更改成员会破坏向后兼容性。
  3. 添加成员会破坏向前兼容性(一些人只认为“添加成员”是安全的,但它确实存在相关风险)。
  4. 每次构建时更改文件版本,您将在某个时候需要它。
  5. 考虑编写定义您的“公共 API”的契约。这些将是您需要在组织外部支持的成员。把它们看作是互操作边界。然后,允许您的实现类具有公共成员,这些成员不在 API 中(因此被视为“不受支持”),因此您可以更改它们而不必担心破坏可扩展性 API。扩展 API 包括编写一个新接口(在接口名称中包含版本号),它不派生自先前版本的接口(派生会防止您完全弃用成员,并在实现单个类中的多个接口版本时产生困难)。
  6. 不要忘记属性,对它们的更改可能不会破坏静态兼容性,但可能会影响运行时。

详细说明#1?如果我的Apple.dll依赖于Bear.dll的v1版本,并且我升级到Bear.dll的v1.2版本,并验证Bear.dll不会像其他#2-6一样暴露任何破坏性更改,那么使用Apple.dll的用户不应该遇到任何问题,对吗?只要我确保所有引用的dll在更新引用的DLL时也满足同样的标准,整个链路上都不应该有问题,对吗? - AaronLS
1
嗨,AaronLS。这是一个好问题,我本来应该详细说明第一点的。 如果Apple.dll的公共功能返回在Bear.dll中声明的类型,则会出现风险。如果这些程序集是强类型的,那么依赖于Apple的产品将期望该函数调用返回(完全限定类型名称)"Type,Bear,v1.1"。如果您已经“热修复”(更改但未重新版本化)了Apple.dll,则现在它将返回“Type,Bear,v1.2”,运行时将抛出类转换异常-因为强命名类型由所有名称组件(包括版本)限定。 - Adam

2

单元测试。它们提供了一种断言“这就是客户端代码所期望的”的方式。你可以在构建时让TFS运行单元测试。(参考链接)


6
这里有两个问题(我非常支持单元测试!)第一个是你需要通过约定来强制实现100%的覆盖率。第二个问题是,除非测试被维护在解决方案之外,任何破坏性的重构都会改变单元测试,从而忽略了这个问题。 - jalbert
不幸的是,单元测试只能发现源代码破坏性更改,而不能发现二进制破坏性更改(例如向方法签名添加可选参数)。 - Heinzi

2
帕特里克·斯马基亚是NDepend软件的知名人士,大约3.5年前他发布了这篇文章。

http://codebetter.com/patricksmacchia/2008/01/20/avoid-api-breaking-changes/

他提到了LibCheck和(显然)NDepend,并且有一个评论提到了另一个工具。由于已经过去3.5年以上了,现在可能有更好的选择(LibCheck已经超过6年了),但这些应该是一个开始。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接