C# Code Contracts: How to validate parameters of interfacemethod defined in other assembly?
I have a situation I don't know how it's supposed to be solved.
According to the user manual section 3, a contractmethod, i.e. Require
or Ensure
, is not allowed in overriding methods/properties or interface implementations. The contract methods should be declared in the root virtual/abstract method and since you can't declare code in an abstract method, you have to work with the ContractClassAttribute
and ContractClassForAttribute
. The same goes for contractmethods for interfacemembers(and their implementations).
But what if I want to use an interface I haven't created myself? For example IL开发者_运维百科ist<T>
doesn't implement these contract methods, but I can't set the ContractClassAttribute
on it either. How should I do parametervalidation in for example the implementation of IList<T>.this[int]
? The following solutions aren't allowed:
T IList<T>.this[int i]
{
get
{
Contract.Requires(i >= 0);//not allowed
if (i < 0)
throw new ArgumentException();
Contract.EndContractBlock();//also not allowed
...
}
}
Are the legacy if-then-throw statements without EndContractBlock
the only solution?
First of all, IList<T> already specifies that precondition. I just tried it to be sure: "Precondition failed: index >= 0".
Furthermore, as I understand it, post conditions are allowed, only preconditions aren't.
If, you would still like to add other preconditions, I believe this answers your question: http://social.msdn.microsoft.com/Forums/en/codecontracts/thread/af403bbc-ca4e-4546-8b7a-3fb3dba4bb4a
It basicly comes down to the fact that you are not meant to do so, as that would defy the purpose of code contracts.
The reason you can't add preconditions is the same reason you can't do this:
interface IInterface
{
void Foo(Parent x);
}
class Implementation : IInterface
{
void Foo(Child x);
}
It's related to (co-/contra-)variance — method parameters are contravariant, meaning the type you accept has to be "at least as big/liberal" as the type that the interface specifies.
Adding more pre-conditions is making the type smaller/stricter, in the same way that accepting a subclass of the type would be (like in the example above).* You can't do this for obvious reasons — a class which implements the interface might not work when passed the correct arguments! If you flipped Parent
& Child
it would be ok.
With method return types it's the other way around: they're covariant, meaning you have to be "at least as small/strict" as the type the interface specifies. This is why you can add post-conditions, because you're making the type "smaller/stricter". It's the same reason why this works:
interface IInterface
{
Parent Foo();
}
class Implementation : IInterface
{
Child Foo();
}
* (This actually raises the question of whether you should be able to remove pre-conditions from implementing classes. This would be an interesting feature.)
精彩评论