Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Create code contracts for a legacy library

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

like image 751
Fyodor Soikin Avatar asked Oct 22 '10 16:10

Fyodor Soikin


2 Answers

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.

like image 72
Sebastian Good Avatar answered Nov 09 '22 06:11

Sebastian Good


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.

like image 44
koenmetsu Avatar answered Nov 09 '22 08:11

koenmetsu