розуміння приватних установ


93

Я не розумію необхідності мати приватних сетерів, які починалися з C # 2.

Наявність методу сеттера для мене дозволяє користувачеві встановлювати деякі змінні в цьому класі. Роблячи це, ми не будемо виставляти змінні безпосередньо користувачам. Натомість ми дозволяємо їм це робити за допомогою цього методу загальнодоступного сеттера.

Для мене це використання "інкапсуляції". Є кілька аргументів, які стверджують, що приватні установники дозволять вам застосовувати інкапсуляцію.

Чи я не використовую інкапсуляцію, використовуючи загальнодоступні методи встановлення? Навіщо нам приватні сетери?

У чому різниця між незмінним класом та класом із приватними сетерами?


1
Я дуже любив приватних сетерів - допоміг мені переробити негарні заняття. Вони також роблять неможливим оголосити і встановити непостійний змінний екземпляр відразу так: private File settingsFile = null;а потім в одному з конструкторів: if (settingsFile == null) { settingsFile = GetSettingsFile() };. Такий код рефакторингу змушував мене іноді плакати :). Те, що ви можете встановити члена перед конструктором, не означає, що вам слід це робити, оскільки для декількох конструкторів це ТВЕРДО слідувати логіці. Приватні установники змушують встановлювати значення всередині конструктора або пізнішої версії.
Hamish Grubijan

Відповіді:


268

Логічно.

Наявність приватного сеттера обумовлена ​​тим, що ви можете використовувати властивість auto:

public int MyProperty { get; set; }

Що б ви зробили, якщо хочете зробити це лише для читання?

public int MyProperty { get; }

Ось лайно!! Я не можу отримати до нього доступ із власного класу; Я повинен створити його як звичайну властивість:

private int myProperty;
public int MyProperty { get { return myProperty; } }

Хм ... але я втратив функцію "Авто властивість" ...

public int MyProperty { get; private set; }

AHHH .. це краще !!


1
Дякую. Це знову має сенс
Дене

4
@ktutnik Дякуємо, що виклали це так, як це зробили. Це має сенс і для мене зараз!
Vivek M. Chawla

4
Чудово проілюстрована відповідь.
імнк

3
Oh crap!! I can't access it from my own classПочинаючи з C # 6.0, це справедливо лише за межами фази ініціалізації. Дивіться мою відповідь stackoverflow.com/a/34223746/198797
tsemer

1
Додавання до відповіді # tsemer із c # 6 {get; }НЕ еквівалентно { get; private set; }. За першим способом property.GetSetMethod(true)повертається nullі другий true. Це мене здивувало.
emragins

37

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

Тому:

public int MyProperty
{
    get; private set;
}

те саме, що:

private int myProperty;
public int MyProperty
{
    get { return myProperty; }
}

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

Щоб відповісти на ваше останнє запитання, MSDN має сказати про приватних установ:

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

З сторінці MSDN на Авто Реалізовано властивості


1
Мені шкода, що я все ще не бачу доданої вартості через наявність приватних установ. Якщо ми не хочемо виставляти сетера, ми маємо лише геттер. Якщо ми хочемо додати валідатор, ми можемо мати загальнодоступний сеттер і додати валідацію до нього. Навіщо нам сетер, який недоступний? Я розумію це як "Візьми цю машину, але ти не можеш їздити на ній", чому ти хочеш дати мені машину, якщо я все одно не збираюся їздити на ній
Дене,

@Dene - Ви, звичайно, можете це зробити, оскільки це не неправильно. Наявність автоматично реалізованих властивостей не є обов’язковим.
ChrisF

Я знаю, що немає нічого поганого у тому, як я це висловив. Це лише для мене, щоб оцінити покращення, зроблене C # 2. Здається, навколо цього багато галасу, але я просто не можу цього відчути чи побачити значення.
Дене

@Dene, я пропустив усі галаси про це. Але, коли я нарешті побачив, що це можна зробити, я був щасливий, бо знав, як очистити кілька тривалих занять з епохи .Net 1.1. Хоча ви, ймовірно, можете використовувати значення властивості, яке ще не встановлено, робити це менш природно, ніж використовувати значення змінної-члена екземпляра, якому встановлено значення null.
Hamish Grubijan

Це спрощує код. Так само, як і властивості авто. Багато подальших оновлень C # стосуються того, щоб зробити код більш стислим і, отже, читабельним. Ви все ще можете робити все по-іншому, якщо хочете - зворотна сумісність також, здається, є великою метою. Думаю, це справа смаку.
niico

18

Це досить просто. Приватні установлювачі дозволяють створювати загальнодоступні або захищені властивості лише для читання.

Це воно. Це єдина причина.

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

Це правда, що приватні сеттери не створювались спеціально для автоматично реалізованих властивостей лише для читання, але їх використання трохи езотеричніше з інших причин, в основному зосереджуючись навколо властивостей лише для читання та використання відображення та серіалізації.


2
Дякуємо, "якщо ви хочете, щоб автореалізована властивість була лише для читання, ви повинні використовувати приватні налаштування". Для мене це має сенс
Дене

18

З введенням C # 6.0 та синтаксисом для автоматичних ініціалізаторів властивостей приватні сеттери більше не потрібні для властивостей, які встановлюються лише під час ініціалізації, вбудовані або в конструкторі.

Тепер нові синтаксиси компілюються:

Вбудоване ініціалізоване властивість

public class MyClass1 {
  public string MyProperty { get; } = "Aloha!"
}

Властивість ініціалізованого конструктором

public class MyClass2 {
  public string MyProperty { get; }

  public MyClass2(string myProperty) {
    MyProperty = myProperty;
  }
}

3
@Ziggler, насправді це не так. Також це не запитує ОП. Він просто не розуміє необхідності їх мати. Це відповідає: "Вам більше не потрібно мати їх за цим сценарієм".
tsemer

6

Я не розумію необхідності мати приватних сетерів, які починалися з C # 2.

Наприклад, клас рахунків-фактур дозволяє користувачеві додавати або видаляти елементи з властивості Items, але він не дозволяє користувачеві змінювати посилання на елементи (тобто користувач не може призначити властивість Items іншому екземпляру об'єкта списку елементів).


public class Item
{
  public string item_code;
  public int qty;

  public Item(string i, int q)
  {
    this.item_code = i;
    this.qty = q;
  }
}

public class Invoice
{
  public List Items { get; private set; }

  public Invoice()
  {
    this.Items = new List();
  }
}

public class TestInvoice
{
  public void Test()
  {
    Invoice inv = new Invoice();
    inv.Items.Add(new Item("apple", 10));

    List my_items = new List();
    my_items.Add(new Item("apple", 10));

    inv.Items = my_items;   // compilation error here.
  }
}

+1 для висвітлення того, що властивостями можна маніпулювати за допомогою загальнодоступного геттера, навіть якщо вони мають приватний сеттер.
user1725145

4

Скажімо, наприклад, ви не зберігаєте фактичну змінну через властивість або використовуєте значення для обчислення чогось.

У такому випадку ви можете створити метод для обчислення

private void Calculate(int value)
{
 //...
}

Або ви можете зробити це, використовуючи

public int MyProperty {get; private set;}

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

Крім цього, якщо навіть сказати, що ви зіставляєте властивість із змінною. У такому випадку в коді ви хочете написати так:

public int myprop;
public int MyProperty {get { return myprop;}}

... ...

this.myprop = 30;

... ...
if(this.MyProperty > 5)
   this.myprop = 40;

Наведений вище код виглядає жахливо, оскільки програмісту завжди потрібно обережно використовувати MyProperty для Get та myprop для Set.

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


3

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


2

Інкапсуляція означає, що стан об'єкта відбувається лише через визначений інтерфейс, і завдяки цьому клас може переконатися, що цей стан завжди є дійсним і відповідає цілям класу.

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

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

В інших випадках, іншими мовами, можна було б отримати метод getter та setter, щось на зразок int getId()отримання значення та void setId(int val)оновлення його.

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

(Насправді, завдяки тому, як працює відображення, і таким випадкам DataBinder.Evalможе бути зручно мати властивість, навіть коли поле буде працювати нормально, але це вже інша справа).

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

Інкапсуляція - це питання не того, чи є ваші установники (або отримувачі) державними, приватними, захищеними чи внутрішніми, а питанням того, чи є вони доречними . Почніть із за замовчуванням кожного поля, яке є приватним (і з цього приводу readonly), а потім за необхідності додайте члени (властивості чи методи), які змінюють ці поля, і переконайтесь, що об’єкт залишається дійсним у міру їх зміни . Це гарантує збереження інваріанта класу , а це означає, що правила, що описують дійсний набір станів, в яких він може перебувати, ніколи не порушуються (конструктори також допомагають, переконавшись, що він починається в такому допустимому стані).

Що стосується вашого останнього питання, бути незмінним означає, що клас не має загальнодоступних, захищених чи внутрішніх установників та жодних загальнодоступних, захищених чи внутрішніх методів, які змінюють будь-які поля. Існують такі ступені, у C # можливі три ступені:

  1. Усі поля екземпляра класу є readonly, отже, навіть приватний код не може його змінити. Він гарантовано буде незмінним (все, що намагається змінити, не компілюється) і, можливо, оптимізація може бути зроблена на звороті цього.

  2. Клас незмінний ззовні, оскільки жоден публічний учасник нічого не змінює, але не гарантується використанням, readonlyщоб він не змінювався зсередини.

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


1

Вам потрібен приватний сеттер, якщо ви хочете підтримати наступний сценарій (не тільки для цього, але це повинно вказати на одну вагому причину): Ви маєте Властивість, яка доступна лише для читання у вашому класі, тобто змінювати можна лише сам клас це, але воно може змінити його після побудови екземпляра. Для прив'язок вам тоді потрібно буде запустити PropertyChanged-подію, переважно це слід робити в установчику властивостей (private). Насправді, ви можете просто запустити подію PropertyChanged-з будь-якого місця в класі, але використання приватного сеттера для цього - це "належне громадянство", оскільки ви не розповсюджуєте тригери властивостей-змін по всьому класу, а тримаєте його на майно, де воно належить.


1

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

Незмінний клас - це клас, який не змінюється після його створення, тому для захисту властивостей потрібні приватні сетери (або взагалі відсутні).

Приватні установники почали частіше використовувати скорочення власності, яке було введено в C # 3. У C # 2 встановлення часто просто опускали, а приватні дані отримували безпосередній доступ при встановленні.

Ця властивість:

public int Size { get; private set; }

те саме, що:

private int _size;
public int Size {
  get { return _size; }
  private set { _size = value; }
}

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

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


Якщо я правильно пам'ятаю, ви не могли мати різні модифікатори доступу на версії C # 2.0 getі setраніше. Крім того, я думаю, ви змішуєте 2.0 та 3.0, оскільки автоматично реалізований стенограф, на який ви посилаєтесь, був 3.0.
Ентоні Пеграм,

1

Я не розумію необхідності мати приватних сетерів, які починалися з C # 2.

Приклад використання:

У мене є екземпляр об’єкта програми, 'UserInfo'що містить властивість, SessionTokenIDV1яку я не хочу надавати споживачам свого класу.

Мені також потрібна можливість встановити це значення зі свого класу.

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

public class UserInfo
{
   public String SessionTokenIDV1 { get; set; }

}


public class Example
{
  // Private vars
  private UserInfo _userInfo = new UserInfo();

  public string SessionValidV1
  {
    get { return ((_userInfo.SessionTokenIDV1 != null) && (_userInfo.SessionTokenIDV1.Length > 0)) ? "set" : "unset"; }
    private set { _userInfo.SessionTokenIDV1 = value; }
  }
}

Редагувати: Редагування фіксованого тегу коду: у прикладі були виправлені помилки


-1

Кредити на https://www.dotnetperls.com/property .

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

public class MyClass
{
    public MyClass()
    {
        // Set the private property.
        this.Name = "Sample Name from Inside";
    }
     public MyClass(string name)
    {
        // Set the private property.
        this.Name = name;
    }
    string _name;
    public string Name
    {
        get
        {
            return this._name;
        }
        private set
        {
            // Can only be called in this class.
            this._name = value;
        }
    }
}

class Program
{
    static void Main()
    {
        MyClass mc = new MyClass();
        Console.WriteLine(mc.name);

        MyClass mc2 = new MyClass("Sample Name from Outside");
        Console.WriteLine(mc2.name);
    }
}

Будь ласка, дивіться знімок екрана нижче, коли я намагався встановити його поза межами класу.

введіть тут опис зображення

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