Подтвердить что ты не робот

С#, неизменяемость и общедоступные поля readonly

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

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

Любые мысли об этом, я что-то упускаю?

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

//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; }), но это только дает вам "согласованную неизменность", а не "гарантированную неизменность"...

4b9b3361

Ответ 1

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

public T2 Item2 { get; readonly set; } 

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

Это на самом деле функция, которую запросили многие люди, поэтому позвольте надеяться, что она скоро появится в гипотетической новой версии С#.

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

Ответ 2

С# 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");

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

Ответ 3

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

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

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

Ответ 4

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

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

Ответ 5

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

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