Как реализовать свойство только для чтения - PullRequest
69 голосов
/ 12 октября 2010

Мне нужно реализовать свойство только для чтения для моего типа. Более того, значение этого свойства будет установлено в конструкторе, и оно не будет изменено (я пишу класс, который предоставляет настраиваемые маршрутизируемые команды пользовательского интерфейса для WPF, но это не имеет значения).

Я вижу два способа сделать это:

  1. class MyClass
    {
        public readonly object MyProperty = new object();
    }
    
  2. class MyClass
    {
        private readonly object my_property = new object();
        public object MyProperty { get { return my_property; } }
    }
    

С учетом всех этих ошибок FxCop, говорящих о том, что у меня не должно быть открытых переменных-членов, кажется, что второй - правильный способ сделать это. Правильно?

Есть ли какая-либо разница между свойством get only и элементом только для чтения в этом случае?

Буду признателен за любые комментарии / советы / и т.д.

Ответы [ 6 ]

58 голосов
/ 12 октября 2010

Второй способ является предпочтительным вариантом.

private readonly int MyVal = 5;

public int MyProp { get { return MyVal;}  }

Это гарантирует, что MyVal может быть назначено только при инициализации (это также может быть установлено в конструкторе).

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

47 голосов
/ 12 октября 2010

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

Конвенция:
Вы следуете конвенции. В таких случаях, когда различия между этими двумя возможностями относительно незначительны, следование соглашению лучше. Одним из случаев, когда он может вернуться, чтобы укусить вас, является основанный на отражении код. Он может принимать только свойства, а не поля, например, редактор / просмотрщик свойств.

Сериализация
Переход от поля к свойству, вероятно, сломает много сериализаторов. А AFAIK XmlSerializer только сериализует открытые свойства, а не открытые поля.

Использование автообъекта
Еще одно распространенное изменение - использование autoproperty с частным сеттером. Хотя это краткое и свойство, оно не обеспечивает чтение только. Поэтому я предпочитаю другие.

Поле «Только чтение» является самодокументированным
Есть одно преимущество в этой области:
С первого взгляда на общедоступный интерфейс становится ясно, что он на самом деле неизменен (за исключением отражения). Принимая во внимание, что в случае свойства вы можете видеть только то, что вы не можете изменить его, поэтому вам придется обратиться к документации или реализации.

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

C # 6.0 добавляет только автоматические свойства для чтения

public object MyProperty { get; }

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

40 голосов
/ 07 мая 2015

С введением C # 6 (в VS 2015) теперь вы можете иметь get -только автоматические свойства, в которых неявное поле поддержки равно readonly (то есть значения могут быть назначены в конструкторе, но не в другом месте):

public string Name { get; }

public Customer(string name)  // Constructor
{
    Name = name;
}

private void SomeFunction()
{
    Name = "Something Else";  // Compile-time error
}

И теперь вы также можете инициализировать свойства (с установщиком или без него) inline:

public string Name { get; } = "Boris";

Возвращаясь к вопросу, это дает вам преимущества варианта 2 (открытый член - это свойство, а не поле) с краткостью варианта 1.

К сожалению, он не обеспечивает гарантию неизменности на уровне открытого интерфейса (как в @ CodesInChaos о самосохранении.документации), поскольку для потребителя класса отсутствие установщика неотличимо от наличия частного установщика.

11 голосов
/ 12 октября 2010

Вы можете сделать это:

public int Property { get { ... } private set { ... } }
5 голосов
/ 12 октября 2010

Я согласен, что второй способ предпочтительнее.Единственная реальная причина этого предпочтения - это общее предпочтение, что классы .NET не имеют открытых полей.Однако, если это поле доступно только для чтения, я не могу понять, как возникнут какие-либо реальные возражения, кроме отсутствия согласованности с другими свойствами.Реальное различие между полем только для чтения и свойством get-only заключается в том, что поле readonly обеспечивает гарантию того, что его значение не изменится в течение срока службы объекта, а свойство get-only - нет.

4 голосов
/ 12 октября 2010

Второй метод предпочтителен из-за инкапсуляции.Конечно, поле readonly может быть общедоступным, но это противоречит идиомам C #, в которых доступ к данным происходит через свойства, а не через поля.

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

...