Как лучше всего использовать открытые поля? - PullRequest
23 голосов
/ 18 декабря 2008

Когда я пишу класс, я всегда открываю приватные поля через публичное свойство, например:

private int _MyField;
public int MyField
{ get{return _MyField; }

Когда можно просто выставить открытое поле, например так:

public int MyField;

Я создаю структуру с именем Result, и я намерен сделать это:

public Result(bool result, string message)
{
   Result = result;
   Message = message;
}

public readonly int Result;
public readonly int Message;

Какая лучшая практика? Это когда-нибудь нормально делать?

Ответы [ 11 ]

31 голосов
/ 18 декабря 2008

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

Под «константой» я подразумеваю любое неизменяемое, неизменяемое значение, а не только то, которое может быть выражено как «const» в C #.

Даже переменные только для чтения экземпляр (например, Результат и Сообщение) должны быть инкапсулированы в свойство, на мой взгляд.

Подробнее см. в этой статье .

17 голосов
/ 18 декабря 2008

Каков наилучший способ использования открытых полей?

«Не делай». Смотри также: Должны ли защищенные атрибуты всегда быть запрещены? , что касается защищенных полей, но то, что сказано, еще более верно для открытых.

9 голосов
/ 18 декабря 2008

Используйте свойства. Теперь легко, когда C # имеет Автоматические свойства !

4 голосов
/ 18 декабря 2008

Лучше всего использовать свойства по нескольким причинам. Во-первых, он отделяет API от базовой структуры данных. Во-вторых, все, что встроено в структуру, которая привязывается к объектам, влияет на свойства, а не на поля.

Я уверен, что есть и другие причины, но мне всегда кажется, что этих двух достаточно.

3 голосов
/ 18 декабря 2008

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

Вот хорошая статья об этом:

http://csharpindepth.com/Articles/Chapter8/PropertiesMatter.aspx

2 голосов
/ 19 декабря 2008

В языках C-Style (например, C #) вы должны выставлять поля через свойства. Это также относится к VB.NET и другим языкам .NET, так как они используют один и тот же базовый движок. Основная причина сделать это состоит в том, что

Публичное MyField как целое число не равно общему свойству MyField как целое число. Они не имеют одинакового поведения во всех ситуациях в .NET.

Однако вы, вероятно, увидите, что многие люди продолжают публиковать поля. Отчасти это связано с тем, как VB6 обрабатывает COM. В VB6 открытый MyField как целое число эквивалентно общему свойству MyField как целое число. Потому что когда оба переводятся в COM-библиотеку типов, они оба реализуются одинаково.

Это привело к множеству странных ограничений в VB6 в отношении того, что может быть публичным полем. Ограничения, которых нет в .NET и многих других объектно-ориентированных языках. Эти ограничения перехватываются во время компиляции, поэтому программист, так сказать, не может выстрелить себе в ноги. Если ваш класс был закрытым в VB6, некоторые из этих ограничений, но не все, были ослаблены. Общее правило состояло в том, что вы можете выставлять только классы и типы данных как открытые поля.

Наследие VB6 означает, что есть много программистов, которые не знают, что есть разница в .NET.

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

В этой статье (на которую указывают другие) объясняется сторона .NET.

2 голосов
/ 19 декабря 2008

Я рекомендую использовать что-то похожее на это:

public class Result{  
    public bool Result {  get; protected set; }
    public string Message {  get; protected set; }

    public Result(bool result, string message) {
        Result = result;
        Message = message;
    }
}

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

1 голос
/ 19 декабря 2008

Я хотел бы ответить на ваш подход только для чтения.

Readonly - это не способ получить доступ только для get в открытом поле. В основном я использую readonly только для приватного поля, где моё приватное поле может быть установлено только из конструктора. Таким образом, чтобы понять, только для чтения поле может быть установлено ТОЛЬКО в конструкторе, и тогда вы можете только получить доступ к нему.

Рекомендуется всегда использовать Свойства для доступа к вашим полям после конструктора. так что в случае, если вам потребуется доступ к вашим свойствам из вашего класса, я бы поставил:

private readonly int result;
private readonly int message;

public Result(bool result, string message)
{
   this.result = result;
   this.message = message;
}

public int Result
{
   get{ return result; }
   private set { result = value; }
}

public int Message
{
   get{ return message; }
   private set { message = value; }
}

Таким образом, вы можете только читать Результат и Сообщение и по-прежнему писать в него из класса.

Если вы используете наследование, вы можете при необходимости защитить набор.

РЕДАКТИРОВАТЬ : После прочтения кода, который я сделал на основе того, что было дано в вопросе, возникает ошибка, при которой имя класса Result, вероятно, выдаст ошибку со свойством Result, а также факт получение bool как результата и строки как сообщения в конструкторе, но попытка отправить их в int это определенно не будет работать. Но для чего-то здесь стоит нечто более логичное:

private readonly bool result;
private readonly string message;

public Answer(bool result, string message)
{
   this.result = result;
   this.message = message;
}

public bool Result
{
   get{ return result; }
   private set { result = value; }
}

public string Message
{
   get{ return message; }
   private set { message = value; }
}
1 голос
/ 18 декабря 2008
0 голосов
/ 17 июля 2012

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

  1. Изменяемая структура с открытыми полями, если каждое из полей будет вести себя как значение с точки зрения типа, и любая произвольная комбинация значений полей будет "иметь смысл". Обратите внимание, что поля, содержащие ссылки на изменяемые типы классов, могут определяться, если цель полей состоит в том, чтобы хранить идентификатор рассматриваемых объектов, а не содержимое. Структуры с изменяемыми полями получили плохую репутацию из-за глупых компиляторов C #, которые интерпретировали бы `SomeThing.SomeStruct.SomeField = what` как` tempStruct = SomeThing.SomeStruct; tempStruct.SomeField = что угодно` без выдачи диагностики, но у программистов сегодня нет причин беспокоиться о том, что сделали бы древние компиляторы.
  2. Неизменяемый класс или структура с непубличными полями, для ситуаций, где вышеупомянутое не применимо, и где необходимость регенерировать весь элемент данных для изменения одного его аспекта не будет чрезмерно утомительной.
  3. Изменчивый класс, если ссылки на него не будут сохраняться вне сущности, которая его создает (такие ссылки, как правило, никогда не должны подвергаться внешнему коду). Обратите внимание, что два других варианта, когда они осуществимы, должны быть значительно предпочтительнее, поскольку коллекция, которая использует изменяемый тип класса в качестве держателя данных (в отличие от коллекции, которая просто хранит идентификаторы объектов, но не волнует, что эти объекты представляют) часто приходится делать защитную копию любого объекта, который хранится в коллекции, а затем делать дополнительную защитную копию каждый раз, когда объект читается. Структуры, как изменяемые, так и «неизменяемые», должны копироваться всякий раз, когда они сохраняются или считываются из коллекции, но такие копии дешевле, для любого размера структуры , чем защитные копии, которые потребуются при использовании изменчивый класс.

Если практической альтернативы использованию изменяемого класса не существует, вопрос о том, следует ли открывать открытые поля, зависит от того, существует ли какая-либо предвидимая потребность в какой-либо версии класса для включения логики проверки в установщик свойств, а также от типа члена будет тип значения или тип класса. Если Bounds является открытым полем типа Rectangle, выражение типа SomeClass.Bounds.Width может получить доступ к Width прямоугольника, не имея доступа к каким-либо другим элементам. Напротив, если бы Bounds было свойством - даже изменяемым - это выражение должно было бы скопировать все четыре члена Bounds во временную структуру, а затем получить доступ к полю Width этого.

...