How do I imply code contracts of chained methods to avoid superfluous checks while chaining?
- by Sandor Drieënhuizen
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?