Интересный вопрос поступил в студию:
в методе класса лучше обращаться к полям этого класса или к свойствам(инкапсулирующим эти поля)? Или зависит от ситуации, допустим в свойстве есть какая-то проверка, тогда через свойство. Если нет проверок, то через поле по идее будет работать быстрее.
Был как-то почти такой вопрос - почему, я против прямого доступа к переменным объекта и я отвечал в комментариях. Сегодня решил ответить в отдельной заметке, чтобы потом проще было находить и ссылаться. В вопросе кроется и ответ - доступ к переменным должен осуществляться ТОЛЬКО через свойства. Никогда не должно быть прямого доступа, даже если вы не делаете никаких проверок. Я понимаю, что впадлу писать get/set и для таких случаев в C# есть офигенная конструкция:
public тип Имя { get; set; }
Например, целочисленное свойства Count может быть объявлено так:
public int Count { get; set; }
Просто, быстро и не нужно писать объявления. Но допустим, что ты объявил переменную: int count и обращаешся к ней напрямую. И вдруг ты понял, что нужно добавить проверку в set и тут начинаешь включать рефакторинг или начинаешь срать в коде:
int _count public int сount { get { return _count } set { проверки _count = value; } }
Теперь count - это свойство, но оно написано с маленькой буквы, хотя везде негласное правило именовать свойства с большое, а переменные с маленькой. Срачь небольшой, но все же есть. Можно изначально назвать переменную с большой буквы на случай если вдруг захотите превратить ее в свойство, но это опять же плохая идея.
А вот если бы ты использовал свойство изначально (public int Count { get; set; }), то его достаточно только развернуть.
Выбор между обращением к свойству или напрямую - эстетический. С практической точки зрения можно выкрутится в любой момент. Но зачем срать в своем коде. Просто приучите себя, чтобы все переменные класса были закрытыми и доступ к ним был только через свойства. Это сделает код красивее и чище.
Updated
И так мы находимся в одном и том же классе и пытаемся получить доступ к переменным этого класса. Делать это через свойство или переменную ведь они обе доступны, даже если переменная private. Я предпочитаю получать доступ через свойства даже внутри класса. Но бывает и обращаюсь и напрямую к переменной. Это не ошибка, поэтому я и написал свой комментарий где-то здесь:
To be clear - использование переменных напрямую не является ошибкой, это просто не красиво с точки зрения ООП. Использование переменных напрямую извне класса, это уже ошибка. Не компиляции, а ошибка проектирования.
Почему я стал предпочитать использовать именно свойства внутри класса. Не раз уже были случаи, когда создаешь свойство какого-то типа, и в нем нужно сделать значение по умолчанию. Это можно делать в конструкторе, но тогда инициализация теряет во времени. А можно делать в свойстве:
Wheel wheel; // можно задать значение по умолчанию здесь, но если оно простое и нет логики public Wheel Wheel
{ get { if (wheel == null) wheel = new Wheel('какие-то параметры, которые могут быть другими свойствами'); return wheel;
} set { проверки wheel = value; } }
Если обращаться к колесу через свойство даже внутри класса, то оно никогда не будет нулевым и будет возвращать значение по умолчанию. В классах, которые работают с базами данных, в getter-е я часто добавляю работу с кешем. Если свойство ссылается на какие-то данные в базе, то я их часто кеширую, поэтому опять же доступ должен быть через свойство даже внутри класса. Или если свойство реально ищет данные в базе данных. В методе get можно вытягивать данные и они будут вытягиваться по мере надобности.
Если вы напишите что-то типа:
object wheel; public int Wheel { get; set; }
При этом будете обращаться к переменной wheel напрямую даже изнутри класса, а потом вдруг решите добавить значение по умолчанию с какими-то расчетами или кеширование, то придется заниматься рефакторингом.
Когда свойства простого типа такие как int, string, bool..., а не объекты, то к ним внутри класса смело обращайтесь напрямую. Случаи, когда нужно будет написать какой-то код в get или set возникают нереально редко. Да и их значения по умолчанию в основном простые данные и их можно задавать так:
int wheel = 0; public int Wheel { get { return wheel; } set { wheel = value; } }
К такому свойству можно обращаться напрямую, если вы уверены, что никогда не нужно будет делать ничего особенного в get или set. Но я стараюсь почти всегда обращаться через свойства даже внутри класса, даже к целочисленным переменным. Просто приучил себя к этому. И когда возникает этот редкий случай, что нужно добавить проверку или изменить какую-то логику свойства, у меня не возникает проблем.
Мой личный опыт показывает, что переменная в классе становится просто хранилищем данных, а доступ лучше осуществлять через свойство, даже внутри класса. Подчеркиваю - лучше, потому что это не является ошибкой. К простым переменным я часто (хотя нет, не часто, но бывает, чаще все же использую свойства) обращаюсь напрямую, поэтому что это можно. Внутри класса по ООП мы можем делать что угодно. Для внешнего мира класс должен быть черным ящиком.
Понравилось? Кликни Лайк, чтобы я знал, какой контент более интересен читателям. Заметку пока еще никто не лайкал и ты можешь быть первым
Ну не скажи, те кто проектировали cocoa и её touch версию с этим были не согласны, а так же со стандартной системой исключений - и это самый приятный и удобный фреймворк с которым я работал.
Хотя в objective-c для таких случаев можно запретить писать/читать переменную вне класса, чего в других языках с ООП не хватает.
У objective-C для доступа к переменным так же создаютя getter и setter. Если ты не хочешь писать их, то может сказать synthesize для своей переменной и тогда компилятор создаст за тебя. Если cocoa обращаются как-то к переменным напрямую, то это их проблемы и не значит, что нужно поступать так же.
To be clear - использование переменных напрямую не является ошибкой, это просто не красиво с точки зрения ООП.
По поводу "Если нет проверок, то через поле по идее будет работать быстрее." это не всегда верно. Есть такое понятие как инлайн подстановка мелкий функций JIT-компилятором при работе без дебага, так что вполне вероятно однострочные get/set функции JIT заменит на прямой доступ к полям.
Спасибо за ответ!
Я сам всегда обращаюсь через свойства, но часто встречаю обращение напрямую(даже у Вас в Библии C#, стр. 90-91).
На хабре встретил мнение по-этому поводу, что не плохо было бы ввести в язык конструкцию полного скрытия поля, допустим так:
public double Speed
{
double speed;
get { ... }
set { ... }
}
Кому интересно, ссылка habrahabr .ru/post/116234/
Никак не могу от C++ переучится, где getter/setter нужно вызывать явно. Постоянно кажется, что доступ идёт напрямую. В C++ это как раз далеко не всегда красиво, особенно если работавший над кодом до программист не заботился о понятности сего действа - это превращается в пытку, ну а если язык полностью объектно ориентированный как C# - согласен, обязательно надо.
klamm, там вроде бы в Библии на стр. 90-91 идет работа с переменными внутри самого класса, и в этом ничего плохого нет. А в сабже, насколько я понял обсуждается доступ к переменным класса извне, который должен предоставляться только через свойства.
Михаил, а все таки, создание свойства-обертки над переменной .NET снижает как-то производительность?
darthat
Как раз и обсуждается доступ изнутри, т.е. из методов этого же класса, снаружи то понятно, что через свойства.
Чтобы сказать точно, нужно посмотреть, какой IL код генерирует компилятор, но мне кажется, что производительность не изменится. Компилятор .NET достаточно интеллектуален. Чуть позже, когда будет время и я вернусь за Windows компьютер я проведу экспериментик.
Писал, писал комментарий, и получилось больше, чем в самой заметке с примерами и объяснениями. Перечитайте заметку начиная со слова updated.
Вот теперь все понятно. Кажется, мелочь, а из таких вот мелочей складывается в результате хороший стиль программирования.
Всё теперь вопрос закрыт, спасибо тестам Zergatul!
Вывод: обращаемся к полям только в get/set свойств, и далее везде используем свойства.
Хотите найти еще что-то интересное почитать? Можно попробовать отфильтровать заметки на блоге по категориям.