С# с использованием сеттеров или геттеров из базового класса

Рекомендуется ли устанавливать переменные-члены базового класса в защищенные, чтобы подклассы могли получить доступ к этим переменным? Или более рекомендуется установить переменные-члены как частные и позволить подклассам получать или устанавливать переменную с помощью геттеров и сеттеров?

И если рекомендуется использовать метод геттеров и сеттеров, то когда используются защищенные переменные?


person Martijn    schedule 12.12.2008    source источник


Ответы (4)


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

Лично я не люблю, чтобы какие-либо поля были неприватными, за редким исключением статических полей только для чтения с неизменяемыми значениями (независимо от того, константны они или нет). Для меня свойства просто обеспечивают лучшую степень инкапсуляции. Способ хранения данных определяется реализацией, а не решением API (в отличие от свойств). Почему класс Foo, производный от класса Bar, должен заботиться о реализации класса Bar?

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

Благодаря автоматически реализованным свойствам в C# 3.0 стало проще, чем когда-либо прежде, превращать поля в свойства. Очень мало причин не этого делать.

person Jon Skeet    schedule 12.12.2008

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

person Daniel Earwicker    schedule 12.12.2008

Я должен согласиться с Джоном.

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

Но установить/получить - это способ сделать для другой ситуации.

person Patrick Desjardins    schedule 12.12.2008
comment
У него другая инкапсуляция — он указывает, что отныне и навсегда эти данные будут храниться в поле с таким именем. Любое изменение этого решения о реализации является критическим изменением. Часто с этим можно жить, но я предпочитаю этого не делать :) - person Jon Skeet; 12.12.2008
comment
Да, то же самое верно для имени Геттера ;) Я предпочитаю не делать этого, но я думаю, что в некоторых случаях это не так уж и плохо ;) - person Patrick Desjardins; 12.12.2008
comment
Однако я бы сказал, что имя получателя является частью API, тогда как выбор хранилища является проблемой реализации. - person Jon Skeet; 12.12.2008

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

Обычно вы должны помечать их как частные и использовать геттеры/сеттеры.

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

РЕДАКТИРОВАТЬ: я выполнил профилирование и, по-видимому, даже в режиме выпуска разница в скорости между полями и свойствами может достигать 20%. См. мой тестовый пример здесь: http://pastebin.com/m5a4d1597

person Tamas Czinege    schedule 12.12.2008
comment
Если в свойстве нет логики, в режиме выпуска JIT в любом случае встроит доступ к свойству, поэтому не должно быть никакого снижения производительности. - person Jon Skeet; 12.12.2008
comment
Я подробно рассказал об этом; нет потери производительности с прямыми (тривиальными) свойствами; в некоторых тестах он действительно стал быстрее, но я подозреваю, что тесту мешали космические лучи ;-p - person Marc Gravell; 12.12.2008
comment
Я пробовал ваш тест (используя секундомер и 10-кратное увеличение итераций, чтобы получить лучшее время). Я не вижу разницы в 20%, но я вижу изменение примерно на 5%, что меня удивляет. Разглядывая его дальше... - person Jon Skeet; 12.12.2008
comment
@Greg: я думаю, что тест в основном разумный. Тот факт, что есть способы ускорить его, не означает, что он не представляет интереса. Компилятор должен иметь возможность встраивать свойства в любом случае. Я попытался сделать поля закрытыми, и свойства не ускорились. - person Jon Skeet; 12.12.2008
comment
(Я еще не пробовал запечатать класс. Это, безусловно, интересно. Сейчас я пробую целые числа вместо двойных.) - person Jon Skeet; 12.12.2008
comment
Re your pastebin: мои результаты (релиз, консоль и т.д.): Поле: 00:00:14.0712500 Свойство: 00:00:14.2500000 Это попадает в шкалу игнорируемых... - person Marc Gravell; 12.12.2008
comment
@Greg: я использую ванильную установку .NET 3.5SP1, скомпилированную в командной строке с помощью csc /o+ /debug- Test.cs - person Jon Skeet; 12.12.2008
comment
На моем немного устаревшем рабочем столе в офисе я неоднократно получаю поле: 05.5xx свойство: 06.5xx. Хорошо, это не 20%, но вы поняли. - person Tamas Czinege; 12.12.2008
comment
Хммм... Окей, происходит что-то странное. Теперь он всегда выполняется ровно за 3,13 секунды для полей или свойств, независимо от того, запечатаны они или защищены, даже с одной и той же командой компиляции. Так что игнорируйте мои предыдущие комментарии... Я их удалю. - person Greg Beech; 12.12.2008
comment
Я получаю около 10% разницы. - person ICR; 14.12.2008
comment
Глядя на дизассемблирование, свойства кажутся встроенными, но порядок некоторых инструкций немного сдвинут (загрузить x, затем mul по y против загрузки y, затем mul по x). Они находятся в разных регистрах, хотя я не вижу, чтобы это вызывало разницу в 5-10%. - person ICR; 14.12.2008
comment
Да, я бы поставил деньги на разные пути оптимизации через эту конкретную реализацию. - person ICR; 14.12.2008
comment
Мой пост о том, откуда берутся дополнительные 5-10%, можно найти по адресу icr.ac.webfusion.co.uk/post/ - person ICR; 15.12.2008