C #, неизменяемость и общедоступные поля только для чтения - PullRequest
34 голосов
/ 12 февраля 2010

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

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

Есть мысли по этому поводу, я что-то упустил?

Пример разницы, для тех, кто читает код легче, чем текст :)

//Immutable Tuple using public readonly fields
public class Tuple<T1,T2>
{
     public readonly T1 Item1;
     public readonly T2 Item2;
     public Tuple(T1 item1, T2 item2)
     {
         Item1 = item1;
         Item2 = item2;
     }
}

//Immutable Tuple using public properties and private readonly fields
public class Tuple<T1,T2>
{
     private readonly T1 _Item1;
     private readonly T2 _Item2;
     public Tuple(T1 item1, T2 item2)
     {
         _Item1 = item1;
         _Item2 = item2;
     }
     public T1 Item1 { get { return _Item1; } }
     public T2 Item2 { get { return _Item2; } } 
}

Конечно, вы можете использовать авто-свойства (public T1 Item1 { get; private set; }), но это только дает вам «согласованную неизменность», а не «гарантированную неизменность» ...

Ответы [ 5 ]

12 голосов
/ 11 сентября 2015

C # 6.0 теперь поддерживает инициализаторы авто-свойств.

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

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

public class Customer
{
    public Customer3(string firstName, string lastName)
    {
        FirstName = firstName;
        LastName = lastName;
    }
    public string FirstName { get; }
    public string LastName { get; }
    public string Company { get; } = "Microsoft";
}

var customer = new Customer("Bill", "Gates");

Подробнее об инициализаторах авто-свойств можно прочитать здесь

11 голосов
/ 12 февраля 2010

Это явное упущение из свойств, что вы не можете написать что-то вроде:

public T2 Item2 { get; readonly set; } 

Я даже не уверен, что readonly - это лучшее слово для обозначения ", которое можно задать только в конструкторе" , но это то, с чем мы застряли.

На самом деле это функция, которую многие люди просили, поэтому будем надеяться, что она будет представлена ​​в гипотетической новой версии C # в ближайшее время.

См. этот связанный вопрос .

4 голосов
/ 12 февраля 2010

В будущем вам может не понадобиться добавлять логику в сеттер, но вам может понадобиться добавить логику в getter .

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

Если я чувствую себя строгим, я бы пошел на полную неизменность (явное readonly вспомогательное поле с открытыми геттерами и без сеттеров). Если я чувствую себя ленивым, то я, вероятно, пойду на «согласованную неизменность» (авто-свойства с открытыми геттерами и частными сеттерами).

0 голосов
/ 12 февраля 2010

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

Также рассмотрим семантику. если T1 является типом значения, а не ссылочным типом, то доступ к obj.Item1 возвращает копию _Item1 в получателе, тогда как доступ к Item1 без получателя не приведет к получению копии. Это означает, что хотя Item1 может быть неизменным внутри, возвращаемый объект типа значения не является. Я не могу придумать причину, по которой это было бы хорошо , но это разница.

0 голосов
/ 12 февраля 2010

Как стандартная практика, я следую вашему 2-му примеру только с использованием «только для чтения», когда объект является общедоступным или уязвимым для случайного вмешательства. Я использую модель «согласованной неизменности» в текущем проекте, создающем структуру плагинов. Очевидно, что при согласованной неизменности защита readonly снята.

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

...