Контракты .NET 4 Code: нужно ли включать одни и те же контракты дважды? - PullRequest
2 голосов
/ 29 июня 2010

Допустим, у меня есть метод public void Foo(string bar), который вызывающий не должен вызывать с нулевым значением bar. Допустим, у меня также есть метод, называемый private void FooImpl(string bar), который выполняет фактическую работу Foo. Это, конечно, FooImpl, что на самом деле требует ненулевого значения bar, хотя Foo является общедоступным интерфейсом. Допустим, я хочу применить ненулевое значение с помощью контрактов кода .NET 4.0.

Куда мне положить контракт?

Если я сделаю это:

public void Foo(string bar)
{
  this.FooImpl(bar);
}

private void FooImpl(string bar);
{
  Contract.Requires<ArgumentNullException>(bar != null);

  // Something that requires non-nullness, e.g.:
  bar.Contains("test");
}

затем статический контролер жалуется, что Foo вызывает FooImpl с возможно нулевым значением, и предлагает добавить ненулевой контракт к Foo. Итак, я полагаю, я не могу делегировать свою проверку контракта / создание исключений методам реализации.

Но если я попытаюсь поместить его в общедоступный интерфейс, т.е.

public void Foo(string bar)
{
  Contract.Requires<ArgumentNullException>(bar != null);

  this.FooImpl(bar);
}

private void FooImpl(string bar);
{
  bar.Contains("test");
}

тогда статический контролер жалуется, что FooImpl вызывает Contains для возможно нулевого значения - хотя единственное место, из которого FooImpl когда-либо вызывается в коде, это Foo, который сам по себе гарантирует, что он никогда не будет вызывать FooImpl с нулевым значением.


Итак, мне нужно включить один и тот же контракт дважды? Или я должен просто игнорировать статическую проверку? Я знаю, что это своего рода источник напряженной работы, и на него нельзя полагаться, но я надеюсь, что у него был какой-то способ справиться с этим базовым и, по-видимому, распространенным сценарием.

Ответы [ 2 ]

3 голосов
/ 29 июня 2010

Краткий ответ: Да.

Вы должны добавлять предварительные условия везде, где вы хотите, чтобы кодовые контракты предотвращали такие вещи, как исключения нулевой ссылки. Иногда это означает, что будет выглядеть так: вы добавляете одни и те же контракты дважды.

В этом конкретном случае для вас очевидно, что FooImpl вызывается только из метода, который уже имеет предварительное условие.

Однако статическая проверка оценивает метод FooImpl независимо от других методов. В этом примере вы просто передаете значение бара из Foo (где вы знаете, что bar не равен null), но статическая проверка не может быть уверена, что вы не манипулировали bar, что может привести к тому, что оно будет нулевым.

Кроме того, вы должны учитывать, что в будущем у вас могут быть вызовы метода FooImpl из методов, которые не имеют предварительных условий для проверки, является ли bar пустым. Вы бы хотели, чтобы статическая проверка также предотвращала исключения исключений в этих случаях.

1 голос
/ 29 июня 2010

Чтобы успокоить статическую проверку, я бы, вероятно, использовал Contract.Assume () для внутренней функции, а не для обычных функций Contract, так что это не будет точной копией существующего контракта - вы не хотитеили нужна реальная проверка во время выполнения в этой внутренней функции.

Почему она недостаточно умна, чтобы сделать вывод, что эта функция вызывается только из другого места, я не уверен.

...