Властивості .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);
    }
}

Вони обоє дають однакові результати. Це ситуація, коли немає правильного і неправильного, і це лише питання переваги?


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 вам потрібно вказати резервну копію, якщо ви використовуєте приватну або отримати або встановити. Так що на vb.net, насправді, менше роботи над тим, щоб зробити власність лише заново, я думаю.
user643192

Я ніколи про це не знав private set. :-)
Афзаал Ахмад Зеешан

9
Оновлення для тих , хто читає цю відповідь в 2016 році C # 6.0 представила тільки для читання авто-властивості, які дозволяють мати властивість тільки для читання без поля підкладки: public string Name { get; }. Якщо ви не хочете змінювати властивість, це зараз є кращим синтаксисом.
Олексій

4
Однією з дуже вагомих причин не користуватися private setє те, що це не так непорушно, як ми любимо робити вигляд. Якщо ви хочете реалізувати справді непорушний клас, лише читання є обов'язковим.
RubberDuck

Це може бути причиною виконання НЕ використовувати лише для читання. Здається, що викликає непотрібне копіювання структур під час доступу до методів структури, що читається тільки. codeblog.jonskeet.uk/2014/07/16/…
Трайнко

28

Використовуйте приватний набір, коли ви хочете, щоб сетерові не можна було отримати доступ ззовні .

Використовуйте лише читання, коли ви хочете встановити властивість лише один раз . У конструкторі або змінному ініціалізаторі.

ТЕСТУЙТЕ ЦЕ:

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
    }
}

Хоча я згоден з вашою відповіддю, ваш приклад міг би використати певне вдосконалення. Ви можете зробити коментар, де сталася помилка компілятора.
Майкл Річардсон

Примітка. Синтаксис 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, тому що ви змінюєте значення під час його встановлення. Як правило, якщо ви встановите значення властивості, ви очікуєте, що повернете те саме значення, коли ви зателефонуєте отримати згодом, що не відбудеться, якби ви зробили ToTitleCase у наборі .

Однак, як ви сказали, немає правильної відповіді.


Я вважаю, що "приватний набір" має особливу семантику в компіляторі (не просто виступає як приватний доступ). Це також стосується захищеного набору? Якщо ні, то де семантичний еквівалент захищеному набору, якщо приватний набір має спеціальну семантику? Я не зміг знайти жодної документації, яка б це пояснювала.
Sprague

1
+1, але я б назвав метод "Перейменувати" замість "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

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

Ваш другий приклад відмовляється від цього у випадку встановлення властивості. Якщо ви використовували такий підхід у великому, складному класі, а пізніше потрібно змінити поведінку власності, ви б опинилися на пошуку та заміні земельної ділянки замість того, щоб вносити зміни в одне місце - приватний налаштування.


2

Щоразу, коли мені потрібно змінити рівень доступу для сеттера, я зазвичай змінював його на Захищений (тільки цей клас та похідні класи можуть змінювати значення) або На друге (лише члени моєї асамблеї можуть змінювати значення).

Але використання Private має ідеальний сенс, коли ви хочете виконувати інші задачі в сетері, крім зміни значення резервного копіювання. Як було зазначено раніше, це гарне проектування не посилатися на ваші резервні значення безпосередньо, а лише отримувати доступ до них через їх властивості. Це гарантує, що пізніші зміни до властивості застосовуються як внутрішньо, так і зовні. І практично немає штрафу за ефективність щодо посилання власності на його змінну резервного копіювання.


0

І штрафу за продуктивність практично немає ...

Але уточнити, доступ до властивості є повільніше , ніж доступ до його змінної підкладковий. Отримувач властивості та налаштування властивості - це методи, які вимагають виклику та повернення, тоді як до змінної резервного копіювання властивості можна отримати доступ безпосередньо.

Ось чому, у випадках, коли до блоку коду ресурсу може бути доступно багато разів, код властивості іноді спочатку кешується (зберігається в локальній змінній), а замість нього використовується локальна змінна. Звичайно, це передбачає, що властивість не може бути змінено асинхронно під час виконання блоку.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.