2011-01-03 4 views
7

В книге coders at work автор спрашивает: «Как вы используете инварианты в своем коде». Пожалуйста, объясните, что означает этот вопрос.Устанавливаются ли инвариантные утверждения в программирование на C#?

Я видел инварианты классов on wiki, но пример в Java, и я недостаточно квалифицирован в Java, чтобы связать этот пример с C#. .NET 4.0 вводит инвариантность, ковариацию и контравариантность и хорошо объясняется. here. Инвариантность настолько широка. Авторы используют слово, похоже, связанное с тестированием. Для тех, кто читает книгу, что означает автор? Мы говорим о том, чтобы сделать предположение и просто проверить правильность после модульного теста?

ответ

10

Слово инвариант не означает больше, чем что-то не меняется при определенных условиях. Существует множество различных инвариантов. Например, в физике скорость света инвариантна относительно lorentz-transform, т. Е. Она не изменяется, если вы переходите к эталонному кадру. В программировании есть много видов инвариантов. Существуют инварианты классов, которые не меняются в течение времени жизни объекта, инварианты метода, которые не меняются в течение срока службы функции, ...

Инвариант класса - это то, что всегда (по крайней мере, при общедоступном наблюдении times) true в экземпляре этого класса.

Это никак не связано с совпадением/несоразмерностью. Co-/Contra-variance описывает, какие типы могут быть заменены на другие типы с разными (родовыми) параметрами или типами возврата. Хотя вы можете назвать что-то инвариантным, потому что оно не поддерживает Co/Contra-variance, это совершенно другой вид инвариантности, чем инвариант класса или метода.

Например какой-то коллекции могут иметь следующие инварианты:

  • данные!= Нуль
  • Размер> = 0
  • Вместимость> = 0
  • Размер < = Емкость

С помощью этого класса:

class MyCollection<T> 
{ 
    private T[] data; 
    private int size; 

    public MyCollection() 
    { 
    data=new T[4]; 
    } 

    public int Size{get{return size;}} 
    public int Capacity{get{return data.Length;}} 

    [ContractInvariantMethod] 
    protected void ClassInvariant() 
    { 
    Contract.Invariant(data != null); 
    Contract.Invariant(Size >= 0); 
    Contract.Invariant(Capacity >= 0); 
    Contract.Invariant(Size < Capacity); 
    } 
} 

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

+1

Свое смешное вы говорите. Инвариантность .NET не связана с другими видами инвариантности. Я не согласен, но, возможно, именно там я ошибаюсь. Возможно, инвариантность типа не связана с классовой инвариантностью и использованием единичного теста или связана только на начальном уровне. Если мы определим «инвариантность», как просто «не изменится». Вопрос: «Как вы проверяете вещи, которые не меняются». Или возникает вопрос: «Как вы проверяете диапазон допустимых значений»? –

+0

@ P.Brian Существует множество типов инвариантов. И дисперсия типа полностью не связана с классовыми инвариантами. Я добавил вступительный параграф. – CodesInChaos

+0

@ P.Brian Чтобы узнать, как принудительно применять инвариант класса в .net 4, см. Ответ «Ли». Если вы это сделаете (и выберите правильные параметры компилятора), ваш код будет переписан, чтобы он тестировал инвариант в конце любого общедоступного метода. – CodesInChaos

2

В этом случае инварианты означают условия, которые применяются к параметрам и которые остаются верными на протяжении всего срока службы функции/метода.

От wikipedia:

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

В .NET инварианты можно проверять/применять с использованием Code Contracts.

+0

Я не думаю, что вам нужно было прочитать книгу, чтобы понять мой вопрос, но способ, которым автор задает вопрос, - это «вопрос факта» или «как вы используете эти вещи», как будто все это делают. Я никогда не сознательно использовал инварианты для каких-либо целей, и в .NET нам кажется, что нам нужно, чтобы атрибут At the wall даже рассматривал их. Итак, обычное использование инвариантов в прошлом или ограничено сборкой/C/C++? –

+0

@ P.Brian.Mackey - Я никогда не использовал их, однако, похоже, что те, кто исходит из фона CS, пытаются использовать их в попытке улучшить «правильность». Я предполагаю, что он предполагает, что все знают об инвариантах и ​​как их использовать - рассмотрите людей, которые представлены в книге :) – Oded

1

Значение в этом контексте не связано с co и contra дисперсией для генериков, введенных в C# 4, а скорее в том же смысле, что и ссылка на статью wikipedia. В C# это могут быть утверждения отладки (т. Е. Debug.Assert(condition)), а также библиотека кодов контрактов. Существует, например, ContractInvariantMethodAttribute, который может быть применен к способу, в классе, который утверждает инварианты класса:

[ContractInvariantMethod] 
protected void ClassInvariant() 
{ 
    Contract.Invariant(someCondition); 
} 
2

Этот пример в вики реализует инварианты с JML, который является очень специфическим, экзотические и, возможно, хорошо но это вовсе не обязательно. Также не просто говорить об инвариантах, а просто говорить об утверждении мутатора, делало то, что ожидалось, чего я не думаю о том, когда я думаю об инвариантах. Я не читал Coders at Work, но я сомневаюсь, что кто-то из Coders at Work использовал JML.

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

Хорошим примером инварианта в коде C# может быть никогда не давать объект N-Hibernate для сохранения, если этот объект не передал его инварианты, которого должно быть много, чтобы предотвратить нечувствительные данные из входя в базу данных. Давайте посмотрим, если я могу думать о каких-либо других примеров ...

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

  • Предположим далее, что объект User «имеет много» объекты электронной почты, и пусть , что он не имеет никакого смысла для Email существовать без владеющему пользователя, то инвариант на классе Email может быть, чтобы ссылка электронной почты на , ее пользователь всегда не имеет значения null, в противном случае у вас есть объект-сирота, который может привести к ошибкам нулевого указателя при попытке обратиться к владельцу электронной почты.

  • Предположим, что вы пишете графический интерфейс, который предполагается представить статус объекта Amazon S3 в обычном WPF форме. Инвариантом в форме может быть удостовериться, что форма должным образом связана с ее объектом перед выполнением любых обработчиков событий на этой форме.

  • Предположим, вы пишете StackOverflow. Инвариантом здесь может быть, чтобы уровень репутации пользователя никогда не был отрицательным, даже , если пользователь принимает штрафные санкции за репутацию. Отрицательная репутация может сломать те симпатичные графики, которые визуализируют как функцию времени (если эти графики не подготовлены , чтобы начертить строки ниже 0-й оси, что они очень может быть ...).

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