为遗留库创建代码契约

发布于 2024-09-29 03:21:25 字数 1125 浏览 1 评论 0原文

最终目标是为驻留在我无法控制的外部程序集中的类指定契约(即我不能直接将契约添加到该类)。

到目前为止我尝试过的:

  1. ContractClassFor 属性。
    不起作用,因为目标类必须指向合约类。

  2. 通过对自动生成的程序集进行逆向工程,手动构建合同引用程序集(即 MyAsm.Contracts.dll)。
    不起作用,因为在我编译它之后,重写器就会启动并方便地删除 ContractDeclarativeAssembly 属性,这使得工具无法将程序集识别为“合同引用程序集”。我找不到关闭重写器的方法。

  3. 创建一个与目标程序集具有相同名称、版本、强名称(如果有)和其他属性的“假”程序集,并在其中放置具有相同名称方法的同名类。然后在打开“构建契约引用”选项的情况下编译该程序集,然后获取生成的契约引用程序集并使用它。
    由于某种原因,这也不起作用,尽管我不知道到底是什么原因。静态检查器只是忽略我的 smartypants 生成的引用程序集,就好像它不存在一样。

还有一件事,以防万一:目标程序集是为 .NET 2.0 编译的,我无法为 4.0 重新编译它。

更新

编写具有已定义契约的“包装器”库是毫无疑问的。

其一,需要编写大量额外代码。但即使您将其放在一边,这也可能是一个重大的性能损失:我必须(可能)为每个用作“遗留”方法的返回类型的“遗留”类型创建包装器类。但即便如此,故事还没有结束。想象一下,某些方法可能返回对基接口的引用,然后调用代码可能会将它们转换为派生接口,以查看该对象是否支持更多功能。我该如何包裹它们?我可能必须在包装类上提供自定义 Cast() 方法,该方法将尝试强制转换底层类/接口,并在成功时返回结果的包装器。最后,我的整个代码库将变得如此复杂,以至于可能不值得。

另一方面,CC 团队本身已经成功解决了这个问题:他们确实为 mscorlib、System.Core 和其他一些系统程序集提供了正确的合同引用程序集,不是吗?他们是如何建造它们的?如果他们能做到,那么我看不出有什么理由我不能完成同样的把戏。

哦,好吧,实际上,我确实看到了一个原因:我不知道该怎么做。 :-) – Fyodor Soikin 18 秒前编辑

The ultimate goal is to specify contracts for a class that resides in an external assembly that I don't have control over (i.e. I cannot just add contracts to that class directly).

What I have tried so far:

  1. ContractClassFor attribute.
    Doesn't work, because the target class must point back to the contract class.

  2. Manually construct a contract reference assembly (i.e. MyAsm.Contracts.dll) by reverse-engineering the autogenerated ones.
    Doesn't work, because right after I compile it, the rewriter kicks in and conveniently strips off the ContractDeclarativeAssembly attribute, which makes the assembly unrecognizable by the tools as "contract reference assembly". And I cannot find a way to turn the rewriter off.

  3. Create a "fake" assembly with same name, version, strong name (if any) and other attributes as the target assembly, and put same-named classes with same-named methods in there. Then compile that assembly with "build contract reference" option turned on, then take the generated contract reference assembly and use it.
    This also didn't work for some reason, although I have no idea what that reason was exactly. The static checker simply ignores my smartypants-generated reference assembly, as if it didn't exist.

And one more thing, just in case it matters: the target assembly is compiled for .NET 2.0, and I cannot recompile it for 4.0.

Update

Writing a "wrapper" library with defined contracts is out of question.

For one, it's a lot of extra code to write. But even if you leave that aside, it may be a significant performance penalty: I would have to (potentially) create wrapper classes for every "legacy" type that is used as return type from my "legacy" methods. But even that is not the end of the story. Imagine that some methods may return references to base interfaces, and then the calling code might cast them to derived interfaces to see if the object supports more functionality. How do I wrap those? I would probably have to provide custom Cast<T>() methods on my wrapping classes, which will try to cast the underlying class/interface and return a wrapper for result if successful. In the end, my whole codebase will become so convoluted that it's probably not worth it anyway.

On the other hand, precisely this problem has already been successfully solved by the CC team itself: they do ship proper contract reference assemblies for mscorlib, System.Core and some other system assemblies, don't they? How did they manage to build them? If they can do it, then I don't see any reason why I shouldn't be able to pull off the same trick.

Oh, well, actually, I do see one reason: I don't know how to do it. :-) – Fyodor Soikin 18 secs ago edit

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

萌能量女王 2024-10-06 03:21:25

您可能已经快要完成(2)了。请务必关闭您手动构建的 C# 程序集的契约重写。关闭所有运行时检查和静态分析,重写器不应启动。将其放在所有其他 CodeContracts 程序集所在的位置,例如,对于 /bin/X.dll,创建一个转到 /bin/ 的程序集CodeContracts/X.Contracts.dll。

请参阅 http://social. msdn.microsoft.com/Forums/en-US/codecontracts/thread/5fe7ad4e-d4f1-4bb5-806e-a1e31f550181。你是对的——只需通过查看 Reflector 创建所有正确的位即可。我想这样做是为了将契约添加到我的 F# 程序集中,直到 F# 可以处理契约。

You were probably almost there with (2). Be sure to turn off contract re-writing for the C# assembly you construct by hand. Turn off all run-time checking and static analysis and the re-writer shouldn't kick in. Put it wherever all your other CodeContracts assemblies are going, e.g. for /bin/X.dll, make an assembly that goes to /bin/CodeContracts/X.Contracts.dll.

See http://social.msdn.microsoft.com/Forums/en-US/codecontracts/thread/5fe7ad4e-d4f1-4bb5-806e-a1e31f550181. You were right -- just create all the right bits by looking at Reflector and it works out. I wanted to do this to add contracts to my F# assembly until F# can handle contracts.

‖放下 2024-10-06 03:21:25

解决方法可能是在代码和外部程序集之间创建一个“外观”层。该层可以保存您将应用于外部代码的所有合同。

如果您不想更改现有代码中的任何内容,恐怕您无能为力。

A work-around could be to create a 'facade' layer between your code and the external assembly. This layer can hold all the contracts that you would apply on the external code.

If you do not want to change anything in the existing code, I'm afraid there's not much more you can do.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文