2010-02-12 5 views
34

Я читал во многих местах, что публиковать поля публично - это не очень хорошая идея, потому что, если вы позже захотите перейти на свойства, вам придется перекомпилировать весь код, который использует ваш класс.C#, неизменяемость и общедоступные поля 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; }), но это только получает вас «согласился неизменность», как против «гарантированной неизменности» ...

ответ

11

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

public T2 Item2 { get; readonly set; } 

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

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

См. this related question.

+2

Hm, «гипотетическая новая версия C#» Кажется, я знаю, кто блог, который вы читали;) – Benjol

+1

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

+2

@ Эрик Липперт, теперь ты просто дразнишь нас. Я ожидаю сообщение о трудностях в ближайшее время :) – Benjol

0

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

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

0

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

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

+0

Что это такое о копировании при возврате через свойство? У вас есть ссылка на это? – Benjol

+0

Типы значений должны копироваться при их передаче. Это работает C#. –

+1

Да, но почему бы и поле не было скопировано при прохождении? Я не вижу, где это будет проблемой. – Benjol

3

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

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

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

11

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

Вы можете прочитать больше о авто-недвижимости инициализаторах here

Смежные вопросы