Когда использовать [Pure] в конструкторе?

19

Я изучаю контракты кода в .NET и пытаюсь понять идею чистых конструкторов. В коде договора документация говорится:

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

И в PureAttributeдокументации говорится:

Указывает, что тип или метод является чистым, то есть он не вносит видимых изменений состояния.

Я понимаю эти утверждения, когда речь идет о методах, но как насчет конструкторов? Предположим, у вас был такой класс:

public class Foo
{
    public int Value { get; set; }

    public Foo(int value) {
        this.Value = value;
    }
}

Этот конструктор, очевидно, влияет на состояние нового Fooобъекта, но у него нет других побочных эффектов (например, он не манипулирует ни одним из параметров и не вызывает какие-либо не чистые методы). Это кандидат на [Pure]или нет? Каково значение размещения [Pure]атрибута в конструкторе, и когда я должен сделать это в своем собственном коде?

PSWG
источник

Ответы:

14

Вы украшаете метод с [Pure]:

  • Если метод не имеет побочных эффектов. Например, если метод обращается к базе данных и изменяет ее или его результат зависит от базы данных, он не является чистым.

  • И если вы планируете использовать его в контрактах кода. Например, если метод чистый , но вы не собираетесь использовать его в контрактах кода, добавление [Pure]не принесет пользы и не сделает ваш код быстрее.

Насколько это касается конструкторов, то это выглядит , как они предполагаются быть чистыми в .NET и не нужен явный атрибут. Я посмотрел на несколько конструкторов в источнике .NET Framework, таких как DateTime, и у них нет [Pure]атрибута.

Я полагаю, это сделано по нескольким причинам:

  • Может быть слишком непрактично писать конструктор без параметров с [Pure]атрибутом, чтобы иметь возможность использовать класс / структуру в контракте.

  • Некоторые, например String, не имеют явных конструкторов.

  • Конструкторы получают специальную обработку даже вне контрактов кода; Например, вы не должны бросать исключения внутри них .

  • [Pure]это просто соглашение, которое здесь для упрощения вашей жизни, но нет никакой реальной статической проверки, чтобы гарантировать, что метод, украшенный этим атрибутом, является чистым. void DestroyDatabase()может быть оформлен как чистый, и кодовые контракты не заметят ничего плохого.

    В настоящее время нет компонента Code Contracts, который проверяет, действительно ли методы, объявленные как pure, чисты. Таким образом, если программист украсил метод с помощью [Pure], он просто поверил.

    Из Кодекса № 5: Метод чистоты

  • .NET Framework содержит конструкторы, которые не являются чистыми. Например, List<T>(IEnumerable<T> collection)на самом деле нечисто, если цикл в коллекции имеет побочные эффекты.

  • Контракты должны быть простыми. Я легко могу себе представить такой контракт, как Contract.Requires(!string.IsNullOrEmpty(name)), поэтому есть веские причины объявлять статическую string.IsNullOrEmptyчистоту.

    С другой стороны, если вам нужно StringBuilderсоздать строку, которую вы потом проверите, вызвав метод экземпляра вашего бизнес-класса, вы, вероятно, неправильно используете контракты. Вот также почему StringBuilder.ToStringне помечен как чистый, даже если это может быть (не так ли?)

Арсений Мурзенко
источник
Многие контракты кода для типов систем предполагаются средством проверки контрактов, включая « любой метод, полное имя которого начинается с« System.Diagnostics.Contracts.Contract »,« System.String »,« System.IO.Path »или« System ». .Тип " ". К сожалению, я не уверен, что просмотр типов .NET слишком полезен, когда речь идет о контрактах кода.
PSWG
3
Чистота - это одна из тех вещей, где весь вызываемый код также должен быть чистым, иначе вызывающая сторона не является «чистой». Мне трудно поверить, что все конструкторы по умолчанию считаются чистыми.
Фрэнк Хайлеман
@FrankHileman: я тоже. У меня сейчас нет компилятора C #, но было бы достаточно написать класс с конструктором и без [Pure]атрибута и использовать его где-то еще в контракте, чтобы получить окончательный ответ.
Арсений Мурзенко
1

Объект не может быть использован, пока он не будет построен в этом случае. Поэтому конструктор чист. Если конструктор вызвал другой код или вызвал делегат, а другой код изменил изменяемое свойство, оно не было бы чистым. Чтобы быть безопаснее, лучше сделать собственность неизменной.

Фрэнк Хилман
источник
Таким образом, чистый конструктор - это чистый метод, которому разрешено изменять состояние текущего класса, если он удовлетворяет другим условиям чистоты? Кстати, свойство изменчиво, потому что я хотел подчеркнуть, что этот класс сам по себе не является чистым.
PSWG
@pswg: Вы создали интересный вопрос, на который, вероятно, должен ответить Microsoft. Предположим, что конструктор вызвал метод, который изменил изменяемое свойство; будет ли конструктор все еще чистым? Я думаю, что технически это не так, хотя модификация «невидима» для внешних зрителей. Поскольку в вашем исходном примере нет другого кода, он должен быть чистым по любому определению, которое я могу придумать.
Фрэнк Хайлеман
@pswg: за исключением того, что набор свойств также является вызовом метода. Я думаю, вы должны спросить на форумах MSDN.
Фрэнк Хайлеман
Я предполагаю, что если центральная идея чистоты заключается в том, вносит ли метод какие-либо наблюдаемые изменения или нет , то в этом смысле, независимо от того, вызывает ли он не чистые методы или нет, пока изменения не могут наблюдаться каким-либо вызывающим объектом, это все равно будет чистый метод.
PSWG
@pswg: это абстрактное определение. Но если бы я писал анализатор для этих вещей, вероятно, считался бы вызов не-чистого метода, чтобы сделать вызывающий объект не чистым. Просто для простоты реализации. Тогда возникает вопрос, является ли конструктор обычным вызовом метода или насколько глубоко идет анализ.
Фрэнк Хайлеман