Почему я не могу добавить Contract.Requires в переопределенном методе? - PullRequest
10 голосов
/ 06 сентября 2011

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

Я сталкиваюсь с чем-то странным для меня ... Я переопределяю метод, определенный в сторонней сборке. Я хочу добавить оператор 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))», поэтому не может добавлять «Требуется».

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

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

Ответы [ 2 ]

14 голосов
/ 06 сентября 2011

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

Рассмотрим:

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

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

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

1 голос
/ 22 января 2014

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

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

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

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

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

...