Почему я не могу добавить Contract.Requires в переопределенном методе?

Я использую кодовый контракт (на самом деле, изучая его).

Я сталкиваюсь с чем-то странным для меня... Я переопределяю метод, определенный в сторонней сборке. Я хочу добавить инструкцию Contract.Require следующим образом:

public class MyClass: MyParentClass
{
    protected override void DoIt(MyParameter param)
    {
        Contract.Requires<ArgumentNullException>(param != null);

        this.ExecuteMyTask(param.Something);
    }

    protected void ExecuteMyTask(MyParameter param)
    {
        Contract.Requires<ArgumentNullException>(param != null);
        /* body of the method */
    }
}

Однако я получаю такие предупреждения:

Предупреждение 1 CodeContracts: Метод 'MyClass.DoIt(MyParameter)' переопределяет MyParentClass.DoIt(MyParameter)) ', поэтому не может добавить Requires.

[edit] немного изменил код, чтобы показать альтернативные проблемы [/edit]

Если я удалю Contract.Requires в методе DoIt, я получаю еще одно предупреждение, говоря, что мне нужно предоставить недоказанный param != null Я не понимаю этого предупреждения. В чем причина, и могу ли я ее решить?

Ответ 1

Вы не можете добавить дополнительные требования, о которых ваши абоненты могут не знать. Он нарушает Принцип подзаголовок Лискова. Точка полиморфизма заключается в том, что вызывающий объект должен иметь возможность обрабатывать ссылку, которая фактически ссылается на экземпляр вашего производного класса, как если бы он ссылался на экземпляр базового класса.

Рассмотрим:

MyParentClass foo = GetParentClassFromSomewhere();
DoIt(null);

Если это статически определено как действительное, неправильно для вашего производного класса задержать руки и сказать "Нет! Вы не должны называть DoIt нулевым аргументом!" Цель статического анализа контрактов заключается в том, что вы можете определить достоверность вызовов, логики и т.д. Во время компиляции... поэтому во время выполнения не могут быть добавлены дополнительные ограничения, что происходит здесь из-за полиморфизма.

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

Ответ 2

Я хотел бы отметить, что вы можете сделать то, что предложили Jon (ответы на него добавляются ), но также и ваш контракт, не нарушая LSP.

Вы можете сделать это, заменив ключевое слово override на new.

База остается базой; все, что вы сделали, представляет другую функциональность (как это обычно предлагают слова).

Это не идеально для статической проверки, потому что безопасность может быть легко отброшена (сначала отбрасывается в базовый класс, а затем вызывается методом), но это необходимо, потому что в противном случае это нарушит LSP, и вы не хотите этого делать явно. Лучше, чем ничего, я бы сказал.

В идеальном мире вы также можете переопределить метод и вызвать новый, но С# не позволит вам это сделать, потому что методы будут иметь одинаковые подписи (даже если это будет иметь смысл, что компромисс).