понимание частных сеттеров


94

Я не понимаю необходимости иметь частные сеттеры, которые начались с C # 2.

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

Для меня это использование «инкапсуляции». Есть некоторые аргументы, утверждающие, что частные сеттеры позволяют применять инкапсуляцию.

Я не использую инкапсуляцию с помощью общедоступных методов установки? Зачем нужны частные сеттеры?

В чем разница между неизменяемым классом и классом с частными сеттерами?


1
Я очень любил частных сеттеров - помог мне перефразировать уродливые классы. Они также делают невозможным объявить и установить непостоянный переменный экземпляр сразу так: private File settingsFile = null;а затем в одном из конструкторов: if (settingsFile == null) { settingsFile = GetSettingsFile() };. Такой рефакторинг кода иногда заставлял меня плакать :). Тот факт, что вы можете установить член перед конструктором, не означает, что вы должны это делать, поскольку при наличии нескольких конструкторов это затрудняет соблюдение логики. Частные сеттеры заставляют вас устанавливать значения внутри конструктора или позже.
— Хэмиш Грубиджан, 03

Ответы:


270

Логически.

Наличие частного установщика связано с тем, что вы можете использовать свойство 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; }

Аааа .. это лучше !!


1
Спасибо. Это снова имеет смысл
— Дене

4
@ktutnik Спасибо, что выложили это так, как вы это сделали. Теперь это имеет смысл и для меня!
— Вивек М. Чавла

4
Прекрасно иллюстрированный ответ.
— imnk

3
Oh crap!! I can't access it from my own classНачиная с C # 6.0 это верно только вне фазы инициализации. См. Мой ответ stackoverflow.com/a/34223746/198797
— Цемер

1
Добавление к ответу # tsemer в C # 6 {get; }НЕ эквивалентно { get; private set; }. По первому способу property.GetSetMethod(true)возвращается nullи второй true. Это меня удивило.
— emragins

38

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

Так:

public int MyProperty
{
    get; private set;
}

такой же как:

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

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

Чтобы ответить на ваш последний вопрос, MSDN говорит следующее о частных установщиках:

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

Со страницы MSDN в автоматически реализованных свойствах


1
Извините, я до сих пор не вижу дополнительных преимуществ из-за наличия частных сеттеров. Если мы не хотим открывать сеттер, у нас есть только геттер. Если мы хотим добавить валидатор, мы можем иметь общедоступный сеттер и добавлять к нему валидацию. Зачем нам недоступный сеттер? Насколько я понимаю, это похоже на «Возьми эту машину, но ты не сможешь ее водить», почему ты хочешь отдать мне машину, если я все равно не собираюсь ее водить
— Ден

@Dene - Ты определенно можешь это сделать, потому что это не так. Автоматическая реализация свойств не является обязательной.
— ChrisF

Я знаю, что нет ничего плохого в том, как я это выразил. Просто мне нужно оценить улучшения, сделанные в C # 2. Кажется, вокруг него много шумихи, но я просто не чувствую этого и не вижу ценности.
— Дене

@ Дене, я пропустил всю эту шумиху по этому поводу. Но когда я наконец увидел, что это возможно, я был счастлив, потому что знал, как очистить некоторые длинные классы из эпохи .Net 1.1. Хотя вы, вероятно, можете использовать значение свойства, которое еще не было установлено, это менее естественно, чем использование значения переменной-члена экземпляра, для которой установлено значение null.
— Хэмиш Грубиджан, 03

Это упрощает код. Прямо как авто свойства. Многие последующие обновления 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, это действительно не так. И OP этого не спрашивает. Он просто не понимает необходимости иметь их. Это дает ответ: «в этом сценарии они больше не нужны».
— Цемер

6

Я не понимаю необходимости иметь частные сеттеры, которые начались с C # 2.

Например, класс счета-фактуры позволяет пользователю добавлять или удалять элементы из свойства Items, но не позволяет пользователю изменять ссылку 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

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

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

Хотя эти случаи в основном ограничиваются классами, которые в основном представляют собой «старые простые данные». Они тоже не очень интересны в этом плане, так что хватит о них.

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

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

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

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

Инкапсуляция - это не вопрос того, являются ли ваши сеттеры (или геттеры) общедоступными, частными, защищенными или внутренними, а вопрос того, подходят ли они . Начните со значения по умолчанию, что каждое поле является частным (и в этом отношении readonly), а затем, при необходимости, добавьте элементы (будь то свойства или методы), которые изменяют эти поля, и убедитесь, что объект остается действительным при их изменении . Это гарантирует, что инвариант класса сохраняется, что означает, что правила, описывающие допустимый набор состояний, в которых он может находиться, никогда не нарушаются (конструкторы также помогают, гарантируя, что он запускается в таком допустимом состоянии).

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

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

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

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


1

Вам нужен частный сеттер, если вы хотите поддерживать следующий сценарий (не только для этого, но это должно указывать на одну вескую причину): у вас есть свойство, которое доступно только для чтения в вашем классе, то есть только самому классу разрешено изменять это, но он может изменить его после создания экземпляра. Для привязок вам нужно будет запустить событие PropertyChanged, предпочтительно это должно быть сделано в (частном) установщике свойств. Фактически, вы можете просто запустить событие 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; }
}

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

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


Если я правильно помню, вы не могли бы иметь различные модификаторы доступа на getи setдо C # 2.0. Кроме того, я думаю, вы смешиваете 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.