Рекомендации по получателям, сеттерам и свойствам. Java против C #


92

Я сейчас изучаю класс C # и пытаюсь найти лучший способ делать что-то. У меня есть опыт работы с Java, поэтому я знаком только с лучшими практиками Java; Я новичок в C #!

В Java, если у меня есть частная собственность, я делаю это;

private String name;

public void setName(String name) {
   this.name = name;
}

public String getName() {
   return this.name;
}

В C # я вижу, что есть много способов сделать это.

Я могу делать это как Java:

private string name;

public void setName(string name) {
   this.name = name;
}

public string getName() {
   return this.name;
}

Или я могу сделать это так:

private string name;

public string Name {
   get { return name; }
   set { name = value; }
}

Или:

public string Name { get; set; }

Какой из них мне следует использовать, и какие нюансы или тонкости связаны с каждым подходом? При создании классов я следую общим передовым методам, которые я знаю по Java (особенно читая «Эффективная Java»). Так, например, я выступаю за неизменность (предоставляю сеттеры только при необходимости). Мне просто любопытно посмотреть, как эти методы сочетаются с различными способами предоставления сеттеров и получателей в C #; по сути, как мне перевести лучшие практики из мира Java на C #?

РЕДАКТИРОВАТЬ

Я публиковал это как комментарий к ответу Джона Скита, но потом он стал длинным:

А как насчет нетривиального свойства (например, со значительной обработкой и проверкой)? Могу ли я раскрыть его через общедоступное свойство, но с логикой, заключенной в getи set? Зачем / должен я делать это, имея выделенные методы установки и получения (со связанной логикой обработки и проверки).

Ответы:


88

Предварительная версия C # 6

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

Неизменяемость - это небольшая проблема с автоматически реализуемыми свойствами - вы не можете написать автоматическое свойство, которое имеет только геттер; самое близкое, что вы можете подойти, это:

public string Foo { get; private set; }

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

private readonly string foo;
public string Foo { get { return foo; } }

Вы точно не хотите писать getName()и setName(). В некоторых случаях имеет смысл писать методы Get / Set, а не использовать свойства, особенно если они могут быть дорогими, и вы хотите это подчеркнуть. Однако вы должны следовать соглашению об именах .NET для методов в PascalCase, и вы не хотите, чтобы такое тривиальное свойство, как это, в любом случае реализовывалось с помощью обычных методов - свойство здесь гораздо более идиоматично.

C # 6

Ура, наконец-то мы получили правильные автоматически реализуемые свойства только для чтения:

// This can only be assigned to within the constructor
public string Foo { get; }

Точно так же для свойств только для чтения, которые действительно должны выполнять некоторую работу, вы можете использовать свойства, содержащие элементы:

public double Area => height * width;

5
Точнее: любой повторный просмотр кода укажет, что способ java - это взлом, который обходит допустимые конструкции времени выполнения langauge AND (!) И убивает использование свойства как свойства (т.е. object.property = "value"). В некоторых командах это привело бы к хорошему разговору об отношении - в зависимости от стажа работы в сочетании со стимулом использовать это отношение к конкурентам. Серьезно, НЕ БОРЬБА С ЯЗЫКОМ. Тем более, что java "путь" - это взлом, выбранный для того, чтобы не изменять язык для поддержки недвижимости.
TomTom

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

4
Чтобы ответить вам, отредактируйте: вы можете использовать методы get / set с любым количеством логики в них, которое вы хотите. Мы часто делаем это, особенно для проверки. Однако лучше всего не использовать медленную логику (например, доступ к базе данных), опасную логику (выброс исключения) или мутацию (изменение большого количества состояний) в свойствах. Ожидается, что свойство будет действовать более или менее как простое состояние. Все остальное следует указывать с помощью функции.
CodexArcanum

2
@rtindru: Да, я в курсе. Вполне возможно написать свойство только для чтения. Но вы не можете объявить автоматически реализуемое свойство без метода доступа set.
Джон Скит


17

Если все, что вам нужно, это переменная для хранения некоторых данных:

public string Name { get; set; }

Хотите сделать его доступным только для чтения?

public string Name { get; private set; }

Или даже лучше ...

private readonly string _name;

...

public string Name { get { return _name; } }

Хотите выполнить некоторую проверку значений перед назначением свойства?

public string Name 
{
   get { return m_name; }
   set
   {
      if (value == null)
         throw new ArgumentNullException("value");

      m_name = value;
   }
}

В общем, GetXyz () и SetXyz () используются только в определенных случаях, и вам просто нужно задействовать свою интуицию, когда вы считаете нужным. В общем, я бы сказал, что я ожидаю, что большинство свойств get / set не будут содержать много логики и будут иметь очень мало, если вообще будут, неожиданных побочных эффектов. Если для чтения значения свойства требуется вызов службы или получение ввода от пользователя для создания запрашиваемого объекта, я бы обернул его в метод и назвал бы как-то вроде BuildXyz(), а не GetXyz().


2
Я бы не выбрасывал исключения в свойствах, я бы предпочел использовать установщики методов с контрактами для определения такого конкретного поведения. Если свойство имеет тип int, я ожидаю, что каждый int будет соответствовать требованиям. Что-то, что вызывает выброс исключения, на мой взгляд, непросто, по моему мнению, вызовы типа INotifyPropertyChanged больше в этой строке.
flindeberg

3
частный сеттер! = immutable
piedar

Пьедар прав. Частный сеттер просто означает, что я не могу выполнять назначения, но я все еще могу использовать myList.Add(), например (пока объект подвергается изменениям, он изменяемый).

1
@flindeberg Извините, но я не согласен ... А как насчет того, если у вас есть индикатор выполнения с Min/ Maxзначением. Вы хотите убедиться в этом Max > Min? Имея SetRange(Min, Max)может иметь смысл , но тогда , как вы будете читать эти значения обратно? Свойства Min / Max только для чтения? Выбрасывание исключений для недопустимого ввода кажется самым чистым способом справиться с этим.
Basic,

12

Используйте свойства в C #, а не методы получения / установки. Они созданы для вашего удобства, и это идиоматично.

Что касается ваших двух примеров C #, один - просто синтаксический сахар для другого. Используйте свойство auto, если все, что вам нужно, - это простая оболочка для переменной экземпляра, используйте полную версию, когда вам нужно добавить логику в геттер и / или сеттер.


5

В C # предпочтение отдается свойствам для отображения закрытых полей для получения и / или установки. Упомянутая вами форма thie - это автосвойство, где операции get и set автоматически создают для вас скрытое поле поддержки сводной таблицы.

Я предпочитаю автоматические свойства, когда это возможно, но вы никогда не должны использовать пару методов set / get в C #.


5
public string Name { get; set; }

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

Все свойства в конечном итоге преобразуются в функции, поэтому фактическая скомпилированная реализация в конечном итоге такая же, как и в Java.

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


4

Независимо от того, какой способ вы выберете в C #, конечный результат будет одинаковым. Вы получите переменную backinng с отдельными методами получения и установки. Используя свойства, вы следуете лучшим практикам, поэтому вопрос о том, насколько подробным вы хотите получить информацию.

Лично я бы выбрал автоматические свойства, последнюю версию : public string Name { get; set; }, поскольку они занимают меньше всего места. И вы всегда можете расширить их в будущем, если вам понадобится добавить что-то вроде проверки.


4

По возможности я предпочитаю общедоступные, string Name { get; set; }поскольку они краткие и легко читаемые. Однако бывают случаи, когда это необходимо.

private string name;

public string Name {
   get { return name; }
   set { name = value; }
}

2
Вы можете объяснить, когда и почему это необходимо?
Jesper

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

3
И почему это «лолз»? Вы можете выразить свою поддержку комментария, проголосовав за.
SquidScareMe

1
Одна из причин использовать явное резервное поле - это когда вы хотите выполнять атомарные / поточно-безопасные операции со значением
Basic

4

В C # предпочтительный путь через свойства , а не getX()и setX()методы. Также обратите внимание, что C # не требует, чтобы свойства имели как получение, так и набор - вы можете иметь свойства только для получения и свойства только для набора.

public boolean MyProperty
{
    get { return something; }
}

public boolean MyProperty
{
    set { this.something = value; }
}

4

Сначала позвольте мне объяснить, что вы написали:

// private member -- not a property
private string name;

/// public method -- not a property
public void setName(string name) {
   this.name = name;
}

/// public method -- not a property
public string getName() {
   return this.name;
}

// yes it is property structure before .Net 3.0
private string name;
public string Name {
   get { return name; }
   set { name = value; }
}

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

С .net framework 3.0

// this style is introduced, which is more common, and suppose to be best
public string Name { get; set; }

//You can more customize it
public string Name
{
    get;
    private set;    // means value could be set internally, and accessed through out
}

Желаю удачи в C #


3

Как уже упоминалось, все эти подходы приводят к одинаковому результату. Самое важное - выбрать условность и придерживаться ее. Я предпочитаю использовать последние два примера свойств.


2

как и большинство ответов здесь, используйте автоматические свойства. Интуитивно понятный, меньше строк кода и более чистый. Если вам следует сериализовать свой класс, отметьте класс [Serializable]/ [DataConract]атрибутом. И если вы используете, [DataContract]отметьте член с помощью

[DataMember(Name="aMoreFriendlyName")]
public string Name { get; set; }

Частный или публичный сеттер зависит от ваших предпочтений.

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

/*this is invalid*/
public string Name 
{ 
    get; 
   /* setter omitted to prove the point*/
}

В качестве альтернативы, если вы хотите только получить / установить, создайте резервное поле самостоятельно


0

Какой из них мне следует использовать, и какие нюансы или тонкости связаны с каждым подходом?

При переходе со свойствами есть одно предостережение, которое еще не упоминалось: со свойствами вы не можете параметризовать свои геттеры или сеттеры.

Например, представьте, что вы хотите получить элементы списка и одновременно хотите применить фильтр. С помощью метода get вы можете написать что-то вроде:

obj.getItems(filter);

Напротив, со свойством вы вынуждены сначала вернуть все предметы

obj.items

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

obj.itemsFilteredByX
obj.itemsFilteredByY

Что иногда может быть неприятно, так это когда вы начали со свойства, например, obj.itemsа затем позже обнаружили, что параметризация геттера или сеттера необходима или может упростить задачу для пользователя класса API. Теперь вам нужно будет либо переписать свой API и изменить все те места в коде, которые имеют доступ к этому свойству, либо найти альтернативное решение. Напротив, с помощью метода get, например obj.getItems(), вы можете просто расширить подпись вашего метода, чтобы принять необязательный объект «конфигурации», например, obj.getItems(options)без необходимости переписывать все те места, которые вызывают ваш метод.

При этом (автоматически реализованные) свойства в C # по-прежнему являются очень полезными сокращениями (по различным причинам, упомянутым здесь), поскольку в большинстве случаев параметризация может не потребоваться, но это предостережение остается в силе.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.