Свойства .NET - Использовать частный набор или свойство ReadOnly?

45

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

Первый пример:

Public Class Person

    Private _name As String

    Public Property Name As String
        Get
            Return _name
        End Get
        Private Set(ByVal value As String)
            _name = value
        End Set
    End Property

    Public Sub WorkOnName()

        Dim txtInfo As TextInfo = _
            Threading.Thread.CurrentThread.CurrentCulture.TextInfo

        Me.Name = txtInfo.ToTitleCase(Me.Name)

    End Sub

End Class

// ----------

public class Person
{
    private string _name;
    public string Name
    {
        get { return _name; }
        private set { _name = value; }
    }

    public void WorkOnName()
    {
        TextInfo txtInfo = System.Threading.Thread.CurrentThread.CurrentCulture.TextInfo;
        this.Name = txtInfo.ToTitleCase(this.Name);
    }
}

Второй пример:

Public Class AnotherPerson

    Private _name As String

    Public ReadOnly Property Name As String
        Get
            Return _name
        End Get
    End Property

    Public Sub WorkOnName()

        Dim txtInfo As TextInfo = _
            Threading.Thread.CurrentThread.CurrentCulture.TextInfo

        _name = txtInfo.ToTitleCase(_name)

    End Sub

End Class

// ---------------

public class AnotherPerson
{
    private string _name;
    public string Name
    {
        get { return _name; }
    }

    public void WorkOnName()
    {
        TextInfo txtInfo = System.Threading.Thread.CurrentThread.CurrentCulture.TextInfo;
        _name = txtInfo.ToTitleCase(_name);
    }
}

Они оба дают одинаковые результаты. Это ситуация, в которой нет правильного и неправильного, и это просто вопрос предпочтений?

tgxiii
источник
public string Name { get; protected set; }через наследство.
Самис

Ответы:

42

Есть несколько причин для использования private set.

1) Если вы вообще не используете вспомогательное поле и хотите использовать автоматическое свойство только для чтения:

public string Name { get; private set; }   

public void WorkOnName()
{
    TextInfo txtInfo = Thread.CurrentThread.CurrentCulture.TextInfo;
    Name = txtInfo.ToTitleCase(Name);
}  

2) Если вы хотите проделать дополнительную работу, когда вы изменяете переменную внутри вашего класса и хотите захватить ее в одном месте:

private string _name = string.Empty;
public string Name 
{ 
    get { return _name; }
    private set 
    {
        TextInfo txtInfo = Thread.CurrentThread.CurrentCulture.TextInfo;
        _name = txtInfo.ToTitleCase(value);
    }
}

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

Адам Лир
источник
1
Просто добавьте это, потому что у вопроса также есть тег vb.net, но в vb.net вам нужно указать покровителя, если вы используете private для get или set. Так что в vb.net на самом деле меньше работы, чтобы сделать свойство доступным только для чтения.
user643192 26.02.13
Я никогда не знал об этом private set. :-)
Афзаал Ахмад Зеешан
10
Обновление для тех , кто читает этот ответ в 2016 году C # 6.0 представила только для чтения авто-свойства, которые позволяют иметь свойство только для чтения без поля подложки: public string Name { get; }. Если вам не нужно изменяемое свойство, это предпочтительный синтаксис сейчас.
Алексей
4
Одна очень веская причина не использовать private setэто то, что она не так неизменна, как нам нравится притворяться. Если вы хотите реализовать действительно неизменяемый класс, необходимо только чтение.
RubberDuck
Может быть причиной производительности НЕ использовать только для чтения. Кажется, вызывает ненужное копирование структур при доступе к методам структурного поля только для чтения. codeblog.jonskeet.uk/2014/07/16/…
Трийнко
28

Используйте закрытый набор, если хотите, чтобы к сеттеру не было доступа извне .

Используйте readonly, если вы хотите установить свойство только один раз . В конструкторе или инициализаторе переменной.

ПРОВЕРЬТЕ ЭТО:

void Main()
{
    Configuration config = new Configuration();
    config.ResetConfiguration();

    ConfigurationReadOnly configRO = new ConfigurationReadOnly();
    configRO.ResetConfiguration();
}

public class Configuration
{
    public Color BackgroundColor { get; private set; }
    public Color ForegroundColor { get; private set; }
    public String Text { get; private set; }

    public Configuration()
    {
        BackgroundColor = Color.Black;
        ForegroundColor = Color.White;
        Text = String.Empty;
    }

    public void ResetConfiguration()
    {
        BackgroundColor = Color.Black;
        ForegroundColor = Color.White;
        Text = String.Empty;
    }
}

public class ConfigurationReadOnly
{
    public readonly Color BackgroundColor;
    public readonly Color ForegroundColor;
    public readonly String Text;

    public ConfigurationReadOnly()
    {
        BackgroundColor = Color.Black;
        ForegroundColor = Color.White;
        Text = String.Empty;
    }

    public void ResetConfiguration()
    {
        BackgroundColor = Color.Black; // compile error: due to readonly keyword
        ForegroundColor = Color.White; // compile error: due to readonly keyword
        Text = String.Empty; // compile error: due to readonly keyword
    }
}
asakura89
источник
Хотя я согласен с вашим ответом, ваш пример может принести некоторые улучшения. Вы можете сделать комментарий, где произойдет ошибка компилятора.
Майкл Ричардсон
NB Синтаксис VB.NET, соответствующий readonlyключевому слову C # , должен применяться ReadOnlyк полю, а не к свойству.
Зев Шпиц
8

Могу ли я предложить третий вариант?

public class Person
{
    public string Name { get; protected set; }

    public void SetName(string name)
    {
        TextInfo txtInfo = System.Threading.Thread.CurrentThread.CurrentCulture.TextInfo;
        this.Name = txtInfo.ToTitleCase(name);
    }
}

Это делает свойство Name эффективным для чтения только для всего внешнего кода и предоставляет явный метод Set. Я предпочитаю явный Set, а не просто использование набора в свойстве Name, потому что вы меняете значение при его установке. Обычно, если вы устанавливаете значение свойства, вы ожидаете получить то же значение обратно при вызове метода get позже, что не произойдет, если вы сделаете свой ToTitleCase в наборе .

Однако, как вы сказали, нет правильного ответа.

Дейв Уайз
источник
Я считаю, что «приватный набор» имеет особую семантику в компиляторе (а не просто как частный метод доступа). Это также случай с защищенным набором? Если нет, то где семантический эквивалент защищенному набору, если частный набор имеет особую семантику? Я не смог найти никакой документации, объясняющей это.
Sprague
1
+1, но я бы назвал метод «Rename» вместо «SetName».
MattDavey
5

Начиная с C # 6.0, в язык были добавлены автоматические свойства только для получателя. Смотрите здесь: https://github.com/dotnet/roslyn/wiki/New-Language-Features-in-C%23-6#getter-only-auto-properties .

Вот пример:

public class SomeClass
{
    public int GetOnlyInt { get; }

    public int GetOnlyIntWithInitializer { get; } = 25;

    public SomeClass(int getOnlyInt)
    {
        GetOnlyInt = getOnlyInt;
    }
}
Даниэль Нил
источник
4

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

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

Carson63000
источник
2

Всякий раз, когда мне нужно было изменить уровень доступа установщика, я обычно менял его на Protected (только этот класс и производные классы могут изменять значение) или Friend (только члены моей сборки могут изменять значение).

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

Prlaba
источник
0

И практически нет потери производительности ...

Но уточнить, доступ к свойству является медленнее , чем доступ к его переменной подкладочный. Получатель и установщик свойства - это методы, которые требуют Call и Return, тогда как к резервной переменной свойства обращаются напрямую.

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

Prlaba
источник