Я пытаюсь понять кодовые контракты немного подробнее. У меня есть следующий надуманный пример, где я пытаюсь утвердить инвариант шаблона try / get: если он возвращает true
, тогда объект out
не равен NULL, в противном случае, если он возвращает false
.
public static bool TryParseFruit(string maybeFruit, out Fruit fruit)
{
Contract.Requires(maybeFruit != null);
Contract.Ensures((Contract.Result<bool>() && Contract.ValueAtReturn(out fruit) != null) ||
(!Contract.Result<bool>() && Contract.ValueAtReturn(out fruit) == null));
// Elided for brevity
if (ICanParseIt())
{
fruit = SomeNonNullValue;
return true;
}
else
{
fruit = null;
return false;
}
}
Мне не нравится дублирование внутри Contract.Ensures
, поэтому я хотел выделить для этого свой собственный метод.
[Pure]
public static bool Implies(bool a, bool b)
{
return (a && b) || (!a && !b);
}
Затем я изменил свой инвариант в TryParseFruit
на
Contract.Ensures(Implies(Contract.Result<bool>(), Contract.ValueAtReturn(out fruit) != null);
Но это порождает предупреждения о том, что "гарантирует, что это не доказано". Если я затем выполняю встроенный рефакторинг для моего Implies
метода, то все снова в порядке.
Может ли кто-нибудь объяснить мне, почему это происходит? Я предполагаю, что это потому, что Contract.ValueAtReturn
каким-то образом используется магическим образом, что означает, что я не могу просто передать его результат другой функции и ожидать, что он будет работать.
(Обновление № 1)
Я думаю, что все следующие Contract.Ensures
выражают одно и то же (а именно, что если функция возвращает true
, тогда fruit
не равен нулю, в противном случае fruit
равно null
). Обратите внимание, что я использую только один из них одновременно:)
Contract.Ensures(Implies(Contract.Result<bool>(), Contract.ValueAtReturn(out fruit) != null));
Contract.Ensures(Contract.Result<bool>() == (Contract.ValueAtReturn(out fruit) != null));
Contract.Ensures(Contract.Result<bool>() ^ (Contract.ValueAtReturn(out fruit) == null));
Однако ни один из вышеперечисленных Contract.Ensures
не приводит к чистой компиляции кода ниже. Я хочу, чтобы Code.Contracts выражал, что fruit.Name
не может быть пустой ссылкой.
Fruit fruit;
while (!TryGetExample.TryParseFruit(ReadLine(), out fruit))
{
Console.WriteLine("Try again...");
}
Console.WriteLine(fruit.Name);
Я могу получить полностью чистую компиляцию с контрактами кода, только если я использую многословный способ выразить это подробно выше. У меня вопрос почему!