Code Contracts [Type] реализует интерфейсный метод {Interface.Method}, поэтому не может добавлять требуемые - PullRequest
21 голосов
/ 05 августа 2010

У меня есть следующий сценарий:

public interface ISomething
{
    void DoStuff();
    //...
}

public class Something : ISomething
{
    private readonly ISomethingElse _somethingElse;
    //...

    public Something (ISomethingElse somethingElse)
    {
         Contract.Requires(somethingElse != null);
        _somethingElse = somethingElse;
    }

    public void DoStuff()
    {
        // *1* Please look at explanation / question below
        _somethingElse.DoThings();
    }
 }

В строке 1 и с включенной статической проверкой я получу предупреждение о том, что _somethingElse, возможно, равно нулю, и если я добавлю контракт, он выдаст мне ошибку

[Тип] реализует интерфейсный метод {Interface.Method}, поэтому не может добавлять требует

Что здесь лучше всего делать? Опции, которые я вижу, включают

  1. пункт охраны, хотя кажется, немного экстремальный
  2. a Contract.Assume
  3. скрытый третий вариант, о котором я не думал

Обратите внимание, поле имеет значение readonly, поэтому после установки значения в конструкторе изменить его невозможно. Таким образом, предупреждение от контрактных кодов кажется немного неактуальным.

1 Ответ

19 голосов
/ 05 августа 2010

Объяснение

В разделе 3: Наследование контрактов в руководстве пользователя говорится, что все предварительные условия должны быть определены в корневом методе цепочки наследования / реализации:

Если клиент удостоверяется, что он выполнил предварительное условие, и имеет переменную o, статический тип которой равен T, то клиент не должен получать нарушение предварительного условия, когда он вызывает o.M.Это должно быть верно, даже если значение времени выполнения o имеет тип U.Следовательно, метод U.M не может добавить предварительное условие, более сильное, чем предварительное условие T.M.

Хотя мы могли бы допустить более слабое предварительное условие, мы обнаружили, что осложнения, связанные с этим, перевешивают преимущества.Мы просто не видели убедительных примеров, когда ослабление предварительного условия полезно.Поэтому мы вообще не разрешаем добавлять какие-либо предварительные условия в подтип.

Как следствие, предварительные условия метода должны быть объявлены в корневом методе цепочки наследования / реализации, т. Е. В первом объявлении виртуального или абстрактного метода,или сам метод интерфейса.

Решение

В вашей ситуации наилучшим способом действий является установка инварианта, утверждающего, что поле _somethingElse никогда не является нулевым:

[ContractInvariantMethod]
private void ObjectInvariant() {
    Contract.Invariant(_somethingElse != null);
}

Это, конечно, всегда верно, поскольку поле помечено readonly и инициализировано в конструкторе.Статическая проверка не может определить это сама по себе, поэтому вы должны явно указать это через этот инвариант.

Вы можете дополнительно добавить постусловие Contract.Ensures(_somethingElse != null); в свой конструктор, но статическая проверка не делаетэто не требуется.

...