Я читал во многих местах, что поля представления публично не являются хорошей идеей, потому что, если Вы позже хотите измениться на свойства, необходимо будет перекомпилировать весь код, который использует класс.
Однако в случае неизменных классов, я не вижу, почему необходимо было бы когда-либо изменяться на свойства - Вы не собираетесь быть добавляющей логикой к 'набору', в конце концов.
Какие-либо мысли об этом, я пропускаю что-то?
Пример различия, для тех, кто прочитал код более легко, чем текст :)
//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; }
), но это только получает Вас 'согласованная неизменность' в противоположность 'гарантируемой неизменности'...
Это очевидное упущение свойств, что вы не можете написать что-то вроде:
public T2 Item2 { get; readonly set; }
Я даже не уверен, что readonly
- лучшее слово, которое можно использовать для обозначения "может быть установлено только в конструкторе", но это то, с чем мы застряли.
На самом деле это функция, о которой просили многие люди, поэтому будем надеяться, что она будет введена в гипотетическую новую версию C# в ближайшее время.
Возможно, вам не понадобится добавлять какую-либо логику в сеттер в будущем, но вы может потребоваться добавить логику в геттер .
Это достаточно веская причина для того, чтобы использовать свойства, а не раскрывать поля.
Если я чувствую себя строгим, я бы выбрал полную неизменяемость (явное только для чтения
поддерживающие поля с открытыми геттерами и без сеттеров). Если мне лень, я, вероятно, выберу «согласованную неизменяемость» (автоматические свойства с открытыми геттерами и частными сеттерами).
В качестве стандартной практики я следую вашему второму примеру, используя 'readonly' только тогда, когда объект является публичным или уязвимым к непреднамеренному вмешательству. Я использую модель 'agreed immutability' в текущем проекте по созданию фреймворка плагинов. Очевидно, что при согласованной неизменяемости защита readonly
снимается.
Только в редких случаях я раскрываю поле - публичное, внутреннее или иное. Это кажется неправильным, если написание свойства {get;} занимает больше времени, чем я готов отдать.
Идея свойств заключается в том, что даже если вы не собираетесь изменять их сейчас или позже, возможно, это может понадобиться каким-то непредвиденным образом. Допустим, вам нужно изменить getter для выполнения какого-либо вычисления или протоколирования. Может быть, вам нужно добавить обработку исключений. Множество потенциальных причин.
Также следует учитывать семантику. Если T1 - тип значения, а не тип ссылки, то обращение к obj.Item1 возвращает копию _Item1 в геттере, тогда как обращение к Item1 без геттера не приведет к получению копии. Это означает, что хотя Item1 может быть неизменяемым внутри, возвращаемый объект типа значения таковым не является. Я не могу придумать причину, по которой это было бы хорошо, но это разница.