Как я могу заключить контракты кода цепных методов, чтобы избежать лишних проверок во время цепочки? - PullRequest
4 голосов
/ 02 мая 2010

Я использую кодовые контракты в C # 4.0. Я применяю обычный статический метод цепочки для имитации необязательных параметров (я знаю, что C # 4.0 поддерживает необязательные параметры, но я действительно не хочу их использовать).

Дело в том, что мои контрактные требования выполняются дважды (или, возможно, количество связанных с ними перегрузок, которые я реализую), если я вызываю метод Init(string , string[]) - очевидный эффект из примера исходного кода ниже. Это может быть дорого, особенно из-за относительно дорогих требований, таких как File.Exists, который я использую.

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.Requires<FileNotFoundException>(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.Requires<FileNotFoundException>(Contract.ForAll<string>(mappingAssemblies, (n) => File.Exists(n)));

    //...
}

Если, однако, я исключаю требования из этого метода, статическая проверка жалуется, что требования перегрузки Init(string, string[], string) не выполнены. Я считаю, что статическая проверка не понимает, что требования перегрузки Init(string, string[], string) неявно применимы и к методу Init(string, string[]); что-то, что можно было бы вычесть из кода IMO.

Вот такую ​​ситуацию я бы хотел достичь:

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));

    //...
}

Итак, мой вопрос таков: есть ли способ автоматически применить требования Init(string, string[], string) к Init(string, string[])?

Обновление

Я использовал ForAll метод неправильно: он предназначен для использования внутри требования или аналогично, например так:

Contract.Requires<FileNotFoundException>(Contract.ForAll<string>(mappingAssemblies, (n) => File.Exists(n)));

Ответы [ 2 ]

1 голос
/ 02 мая 2010

Я не думаю, что это для общего случая.

В вашем конкретном случае вы, безусловно, можете переместить более дорогой ForAll (File.Exists) в метод реальной реализации и не получать предупреждений:

        public static void Init(string configurationPath, string[] mappingAssemblies)
    {
        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");
        Init(configurationPath, mappingAssemblies, null);
    }

    public static void Init(string configurationPath, string[] mappingAssemblies, string optionalArgument)
    {
        Contract.Requires<ArgumentNullException>(mappingAssemblies != null, "mappingAssemblies");
        Contract.ForAll<string>(mappingAssemblies, (n) => File.Exists(n));
    }

Редактировать - я бы забыл сделать это на более ранних уровнях и просто украсил методы атрибутом ContractVerification (). Это не дает мне предупреждений, 7 проверенных утверждений, со всеми включенными опциями статической проверки.

    [ContractVerification(false)]
    public static void Init(string configurationPath, string[] mappingAssemblies)
    {
        Init(configurationPath, mappingAssemblies, null);
    }

    public static void Init(string configurationPath, string[] mappingAssemblies, string optionalArgument)
    {

        Contract.Requires<ArgumentNullException>(mappingAssemblies != null, "mapping assemblies");
        Contract.Requires<ArgumentNullException>(configurationPath != null, "configurationpath");
        Contract.Requires<ArgumentException>(configurationPath.Length > 0, "configurationPath is an empty string");
        Contract.Requires<FileNotFoundException>(File.Exists(configurationPath));
        Contract.Requires<FileNotFoundException>(Contract.ForAll<string>(mappingAssemblies, (n) => File.Exists(n)));


        // ... 
    }
0 голосов
/ 27 сентября 2010

Поскольку вы используете контракты, я предполагаю, что вы используете C # 4.0. Тогда вы можете использовать дополнительные параметры и иметь только одно место для размещения ваших контрактов.

...