How do I imply code contracts of chained methods to avoid superfluous checks while chaining?

Posted by Sandor Drieënhuizen on Stack Overflow See other posts from Stack Overflow or by Sandor Drieënhuizen
Published on 2010-05-02T16:11:46Z Indexed on 2010/05/02 16:17 UTC
Read the original article Hit count: 411

Filed under:
|
|

I'm using Code Contracts in C# 4.0. I'm applying the usual static method chaining to simulate optional parameters (I know C# 4.0 supports optional parameters but I really don't want to use them).

The thing is that my contract requirements are executed twice (or possibly the number of chained overloads I'd implement) if I call the Init(string , string[]) method -- an obvious effect from the sample source code below. This can be expensive, especially due to relatively expensive requirements like the File.Exists I use.

public static void Init(string configurationPath, string[] mappingAssemblies)
{
    // The static contract checker 'makes' me put these here as well as
    // in the overload below.
 Contract.Requires<ArgumentNullException>(configurationPath != null, "configurationPath");
 Contract.Requires<ArgumentException>(configurationPath.Length > 0, "configurationPath is an empty string.");
 Contract.Requires<FileNotFoundException>(File.Exists(configurationPath), configurationPath);
 Contract.Requires<ArgumentNullException>(mappingAssemblies != null, "mappingAssemblies");
 Contract.ForAll<string>(mappingAssemblies, (n) => File.Exists(n));

 Init(configurationPath, mappingAssemblies, null);
}

public static void Init(string configurationPath, string[] mappingAssemblies, string optionalArgument)
{
    // This is the main implementation of Init and all calls to chained
    // overloads end up here.
 Contract.Requires<ArgumentNullException>(configurationPath != null, "configurationPath");
 Contract.Requires<ArgumentException>(configurationPath.Length > 0, "configurationPath is an empty string.");
 Contract.Requires<FileNotFoundException>(File.Exists(configurationPath), configurationPath);
 Contract.Requires<ArgumentNullException>(mappingAssemblies != null, "mappingAssemblies");
 Contract.ForAll<string>(mappingAssemblies, (n) => File.Exists(n));

 //...
}

If however, I remove the requirements from that method, the static checker complains that the requirements of the Init(string, string[], string) overload are not met. I reckon that the static checker doesn't understand that there requirements of the Init(string, string[], string) overload implicitly apply to the Init(string, string[]) method as well; something that would be perfectly deductable from the code IMO.

This is the situation I would like to achieve:

public static void Init(string configurationPath, string[] mappingAssemblies)
{
    // I don't want to repeat the requirements here because they will always
    // be checked in the overload called here.
 Init(configurationPath, mappingAssemblies, null);
}

public static void Init(string configurationPath, string[] mappingAssemblies, string optionalArgument)
{
    // This is the main implementation of Init and all calls to chained
    // overloads end up here.
 Contract.Requires<ArgumentNullException>(configurationPath != null, "configurationPath");
 Contract.Requires<ArgumentException>(configurationPath.Length > 0, "configurationPath is an empty string.");
 Contract.Requires<FileNotFoundException>(File.Exists(configurationPath), configurationPath);
 Contract.Requires<ArgumentNullException>(mappingAssemblies != null, "mappingAssemblies");
 Contract.ForAll<string>(mappingAssemblies, (n) => File.Exists(n));

 //...
}

So, my question is this: is there a way to have the requirements of Init(string, string[], string) implicitly apply to Init(string, string[]) automatically?

© Stack Overflow or respective owner

Related posts about c#

Related posts about c#4.0