Должен ли я предпочесть свойства с частными полями или без них?

16

Кодовая база, в которой я сейчас работаю, имеет соглашение об использовании частных полей и открытых свойств. Например, у большинства классов их члены определены следующим образом:

// Fields
private double _foo;
private double _bar;
private double _baz;

// Properties
public double Foo
{
    get{ return _foo; }
    set{ _foo = value; }
}

public double Bar
{
    get{ return _bar; }
    set{ _bar = value; }
}

public double Baz
{
    get{ return _baz; }
}

Я знаю, что они могут быть переписаны без их внутренних частных свойств:

public double Foo{ get; set; }
public double Bar{ get; set; }
public double Baz{ get; private set; }

Я хотел бы некоторый вклад по этому вопросу:

  • Есть ли веская причина предпочесть более старый, более явный стиль более новому, более лаконичному?
  • Должен ли я написать какие-либо новые классы, используя сжатый стиль, или я должен попытаться сопоставить старый код для согласованности? Достаточна ли последовательность в этом случае, чтобы оправдать старый формат?
KChaloux
источник
Смотрите этот вопрос и ответ .
Питер К.
@PeterK. Информативный. Не отвечает, стоит ли мне беспокоиться о соответствии стилю остальной части программы, или это достаточно маленькая деталь, чтобы не иметь значения.
KChaloux
@KChaloux: Понятно! Вот почему это комментарий, а не ответ. :-)
Питер К.
@PeterK. Удовлетворительное 'nuff = p
KChaloux
1
Еще одна причина, по которой не нужно вносить изменения, заключается в том, что если что-то передается по проводам с помощью WCF - автоматические свойства имеют проблемы с контрактом (из-за недопустимых символов в именах полей поддержки, созданных .NET)
Леон

Ответы:

16

Есть пара случаев, когда так называемый «старый» стиль все еще необходим:

A: Неизменяемые типы, использующие неизменяемость языка. readonlyМодификатор в C # замерзает , что значение после строительства. Там нет способа имитировать это с автоматически реализованными свойствами (пока).

public sealed class FooBarBazInator
{
    private readonly double foo;

    public FooBarBazInator(double foo)
    {
        this.foo = foo;
    }

    public double Foo
    {
        get
        {
            return this.foo;
        }
    }
}

B: Ваши геттеры / сеттеры имеют какую-либо логику. Код WPF и Silverlight (и аналогичный), привязанный к данным для ваших классов, будет реализован INotifyPropertyChangedследующим образом:

public class FooBarBazInator : INotifyPropertyChanged
{
    private double foo;

    public event PropertyChangedEventHandler PropertyChanged;

    public double Foo
    {
        get
        {
            return this.foo;
        }

        set
        {
            this.foo = value;
            this.RaisePropertyChanged("Foo");
        }
    }

    private void RaisePropertyChanged(string propertyName)
    {
        var propertyChanged = this.PropertyChanged;

        if (propertyChanged != null)
        {
            propertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

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

Джесси С. Слайсер
источник
2
Кажется, что все согласны с тем, чтобы перейти на новый стиль. Вы получаете +1 и кредит за ответ за то, что сказали мне о необходимости поля с readonlyмодификатором, которого я не знал. = D
KChaloux
3
Некоторые (включая меня) считают свойства авто с частным средством доступа «достаточно хорошими» для неизменяемых свойств. Правда, они не могут быть действительно неизменными, но вы просто должны быть осторожны, чтобы не использовать сеттер вне конструктора.
svick
2
Еще одна причина, если вы хотите передать значение, refкоторое не работает со свойствами, поэтому вам нужно поле
stijn
1
Инструмент, подобный Fody, может быть реализован INotifyPropertyChangedбез необходимости явных вспомогательных полей. github.com/Fody/PropertyChanged
Себастьян Редл
3
Обратите внимание: C # 6 допускает «авто-свойства только для получателя», которые в одной строке предоставляют то, что делает мой пример «A».
Джесси С. Слайсер
6

Я бы сказал, что наличие явного вспомогательного поля - просто бесполезная затея: он делает ваш код длиннее и труднее для чтения. Это также добавляет вероятность ошибки:

public double Bar
{
    get { return _baz; }
    set { _bar = value; }
}

Я думаю, что такая ошибка может произойти довольно легко, и ее будет довольно трудно обнаружить.

А если вам нужна согласованность, сделайте это по-другому: измените код, который использует вспомогательные поля, на код, который использует автоматические свойства. Это особенно легко, если вы используете инструмент рефакторинга, такой как ReSharper (и если вы не используете что-то подобное, я думаю, вам следует начать).

Конечно, все еще есть случаи, когда наличие вспомогательного поля необходимо, но я думаю, что это не относится к этому вопросу.

svick
источник
Я на самом деле сделал это на нескольких старых полях при рефакторинге кода ... это боль.
KChaloux
1

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

  1. Механизмы сериализации во время выполнения сохраняют имя файла в сериализованном потоке. Имя вспомогательного поля для Автоматически реализуемого свойства (AIP) определяется компилятором, и оно может фактически изменять имя этого вспомогательного поля каждый раз, когда вы перекомпилируете свой код, сводя на нет возможность десериализации экземпляров любых типов, которые содержат AIP. Поэтому не используйте AIP с любым типом, который вы собираетесь сериализовать или десериализовать.

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

ОЗУ
источник
3
# 1 - Большинство сериализаторов игнорируют частные свойства / поля по умолчанию; Я никогда не сталкивался с проблемами, которые вы упоминаете. # 2 - Это исправлено в VS2015 и может быть исправлено в VS2013. Смотрите эту статью Visual Studio Magazine .
Брайан
-3

Есть ли веская причина предпочесть более старый, более явный стиль более новому, более лаконичному?

На самом деле, нет. Хотя я бы сказал, что каждый раз, когда вы проходите через свойство, подобное, вы должны были просто использовать открытое поле для начала.

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

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

Telastyn
источник
@svick - Бах. Если вы не делаете отражение в типе, нет никакой разницы. Если вы публично представляете тип как интерфейс службы или библиотеки, вы собираетесь предоставить интерфейс, который в любом случае будет нуждаться в свойстве. ОП не делает ничего из этого.
Теластин
2
В этих ограниченных случаях нет технической причины. Не забывайте, что свойства ведут себя по-разному в отладчике, диаграммах классов, intellisense, и просто, когда вы думаете, что не выполняете рефлексию, .NET Framework - это. WinForms, WPF и некоторые другие компоненты внутренне используют отражение, и они обрабатывают свойства отдельно, поэтому рекомендации по использованию открытых полей всегда будут плохо приниматься многими разработчиками на этом сайте.
Кевин Маккормик
1
@KevinMcCormick и фреймворк ведут себя превосходно (насколько я понимаю) с полями. На самом деле это не имеет значения, так как теперь есть авто-свойства, но большая проблема заключается в том, чтобы обнародовать информацию в первую очередь. Слишком много людей думают, что просто придание свойств вещам каким-то образом освобождает их от «не делайте общедоступных полей».
Теластин
2
Framework обрабатывает их по-разному: попробуйте использовать открытое поле в EF POCO, привязать его к DataGridView, использовать PropertyGrid и т. Д. Я только что сделал быстрый поиск в своем декомпиляторе Type.GetProperties / GetProperty, и Framework делает вызовы этого метода сотни раз, но не в GetField. В итоге, они разные, и рекомендация всегда использовать свойства для публичных данных частично основана на этом факте. Это не исключает возможности использования полей, но этот случай требует обоснования. Тем не менее, ваша другая точка зрения безусловно верна, небрежное раскрытие общедоступных данных - всегда плохая идея.
Кевин Маккормик