Насколько свободным я могу быть в коде, инвариантном к объекту?

StackOverflow https://stackoverflow.com/questions/1209185

Вопрос

Я пытаюсь продемонстрировать инварианты в кодовых контрактах, и я подумал, что приведу пример отсортированного списка строк.Он поддерживает массив внутри, с запасным пространством для дополнений и т.д. - точно так же, как List<T>, в принципе.Когда ему нужно добавить элемент, он вставляет его в массив и т.д.Я полагал, что у меня есть три инварианта:

  • Граф должен быть благоразумен:неотрицательный и не более такого же размера, как размер буфера
  • Все, что находится в неиспользуемой части буфера, должно быть нулевым
  • Каждый элемент в используемой части буфера должен быть по крайней мере таким же "большим", как элемент перед ним

Теперь я попытался реализовать это таким образом:

[ContractInvariantMethod]
private void ObjectInvariant()
{
    Contract.Invariant(count >= 0 && count <= buffer.Length);
    for (int i = count; i < buffer.Length; i++)
    {
        Contract.Invariant(buffer[i] == null);
    }
    for (int i = 1; i < count; i++)
    {
        Contract.Invariant(string.Compare(buffer[i], buffer[i - 1]) >= 0);
    }
}

К сожалению, ccrewrite это запутывает петли.

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

Contract.Invariant(count >= 0 && count <= buffer.Length);
Contract.Invariant(Contract.ForAll
    (count, buffer.Length, i => buffer[i] == null));
Contract.Invariant(Contract.ForAll
    (1, count, i => string.Compare(buffer[i], buffer[i - 1]) >= 0));

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

Являются ли мои ожидания необоснованными?Являются ли мои инварианты необоснованными?

(Также заданный в качестве вопрос на форуме Code Contracts.Я сам добавлю сюда любые соответствующие ответы.)

Это было полезно?

Решение

На (предварительных) страницах MSDN это выглядит как Контракт.Участник ForAll мог бы помочь вам с контрактами на 2 диапазона.Однако в документации не очень четко описана его функция.

//untested
Contract.Invariant(Contract.ForAll(count, buffer.Length, i => buffer[i] == null));
Contract.Invariant(Contract.ForAll(1, count, 
    i => string.Compare(buffer[i], buffer[i - 1]) >= 0));

Другие советы

(Я собираюсь принять ответ Хенка, но я думаю, что стоит добавить это.)

Теперь на этот вопрос дан ответ в Форум MSDN, и результатом является то , что первая форма это не так ожидалось, что это сработает.Инварианты действительно, действительно должны представлять собой серию вызовов для Contract.Invariant, и это все.

Это делает более выполнимым для статической проверки понимание инварианта и его принудительное применение.

Это ограничение можно обойти, просто поместив всю логику в другой элемент, напримеран IsValid свойство, а затем вызывающий:

Contract.Invariant(IsValid);

Это, без сомнения, испортило бы статическую проверку, но в некоторых случаях это может быть полезной альтернативой в некоторых случаях.

Разве дизайнеры немного не изобретают велосипед?

Что было не так с старый добрый

bool Invariant() const; // in C++, mimicking Eiffel

?

Теперь в C # у нас нет const, но почему вы не можете просто определить Invariant функция

private bool Invariant()
{
  // All the logic, function returns true if object is valid i.e. function
  // simply will never return false, in the absence of a bug
}
// Good old invariant in C#, no special attributes, just a function

а затем просто использовать Кодовые контракты в терминах этой функции?

[ContractInvariantMethod]
private void ObjectInvariant()
{
    Contract.Invariant(Invariant() == true);
}

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top