В своей прекрасной книге CLR Via C # Джеффри Рихтер сказал, что ему не нравятся свойства, и он рекомендует не использовать их. Он объяснил причину, но я не совсем понимаю. Может ли кто-нибудь объяснить мне, почему я должен или не должен использовать свойства? Изменилось ли это в C # 3.0 с автоматическими свойствами?
В качестве справки я добавил мнения Джеффри Рихтера:
• Свойство может быть доступно только для чтения или только для записи; доступ к полю всегда доступен для чтения и записи. Если вы определяете свойство, лучше всего предлагать методы доступа как get, так и set.
• Метод свойства может вызвать исключение; доступ к полю никогда не вызывает исключения.
• Свойство нельзя передать методу как параметр out или ref; поле может. Например, следующий код не будет компилироваться:
using System;
public sealed class SomeType
{
private static String Name
{
get { return null; }
set {}
}
static void MethodWithOutParam(out String n) { n = null; }
public static void Main()
{
// For the line of code below, the C# compiler emits the following:
// error CS0206: A property or indexer may not
// be passed as an out or ref parameter
MethodWithOutParam(out Name);
}
}
• Для выполнения метода свойства может потребоваться много времени; доступ к полю всегда завершается немедленно. Распространенной причиной использования свойств является выполнение синхронизации потока, которая может остановить поток навсегда, поэтому свойство не следует использовать, если требуется синхронизация потока. В этой ситуации предпочтительнее использовать метод. Кроме того, если к вашему классу можно получить удаленный доступ (например, ваш класс является производным от System.MashalByRefObject), вызов метода свойства будет очень медленным, и поэтому метод предпочтительнее свойства. На мой взгляд, классы, производные от MarshalByRefObject, никогда не должны использовать свойства.
• Если вызывается несколько раз подряд, метод свойства может каждый раз возвращать другое значение; поле каждый раз возвращает одно и то же значение. Класс System.DateTime имеет свойство Now только для чтения, которое возвращает текущую дату и время. Каждый раз, когда вы запрашиваете это свойство, оно будет возвращать другое значение. Это ошибка, и Microsoft желает, чтобы они могли исправить класс, сделав Now методом вместо свойства.
• Метод свойства может вызвать наблюдаемые побочные эффекты; доступа к полю никогда не бывает. Другими словами, пользователь типа должен иметь возможность устанавливать различные свойства, определяемые типом, в любом порядке, который он или она выбирает, не замечая какого-либо другого поведения в типе.
• Метод свойства может потребовать дополнительной памяти или вернуть ссылку на что-то, что на самом деле не является частью состояния объекта, поэтому изменение возвращенного объекта не влияет на исходный объект; запрос поля всегда возвращает ссылку на объект, который гарантированно является частью исходного состояния объекта. Работа со свойством, которое возвращает копию, может сбивать с толку разработчиков, и эта характеристика часто не документируется.
источник
Ответы:
Причина, по которой Джефф не любит свойства, заключается в том, что они выглядят как поля - поэтому разработчики, которые не понимают разницы, будут обращаться с ними как с полями, предполагая, что их выполнение будет дешевым и т. Д.
Лично я не согласен с ним в этом конкретном вопросе - я считаю, что свойства делают клиентский код намного проще для чтения, чем эквивалентные вызовы методов. Я согласен с тем, что разработчики должны знать, что свойства - это в основном замаскированные методы, но я думаю, что лучше информировать разработчиков об этом, чем усложнять чтение кода с помощью методов. (В частности, увидев Java-код с несколькими геттерами и сеттерами, вызываемыми в одном операторе, я знаю, что эквивалентный код на C # будет намного проще читать. Закон Деметры очень хорош в теории, но иногда
foo.Name.Length
действительно правильная вещь использовать ...)(И нет, автоматически реализуемые свойства ничего из этого не меняют.)
Это немного похоже на аргументы против использования методов расширения - я могу понять рассуждения, но практическая польза (при умеренном использовании), на мой взгляд, перевешивает обратную сторону.
источник
Что ж, давайте рассмотрим его аргументы один за другим:
Это выигрыш для свойств, так как у вас есть более точный контроль доступа.
Хотя это в основном так, вы вполне можете вызвать метод для неинициализированного поля объекта и вызвать исключение.
Справедливая.
Это также может занять совсем немного времени.
Не правда. Как узнать, что значение поля не изменилось (возможно, другим потоком)?
Если это ошибка, то это мелкая ошибка.
Справедливая.
Большинство протестов можно сказать и о геттерах и сеттерах Java - и они у нас довольно долго были без таких проблем на практике.
Я думаю, что большинство проблем может быть решено за счет лучшей подсветки синтаксиса (т.е. различения свойств от полей), чтобы программист знал, чего ожидать.
источник
_field
. Или просто строчныеfield
.Я не читал книгу, а вы не процитировали ту ее часть, которую не понимаете, поэтому мне придется угадывать.
Некоторым не нравятся свойства, потому что они могут заставить ваш код делать удивительные вещи.
Если я печатаю
Foo.Bar
, люди, читающие его, обычно ожидают, что это просто доступ к полю-члену класса Foo. Это дешевая, почти бесплатная операция, и она детерминирована. Я могу повторять это снова и снова и каждый раз получать один и тот же результат.Вместо этого со свойствами это может быть вызов функции. Это может быть бесконечный цикл. Это может открыть соединение с базой данных. Он может возвращать разные значения каждый раз, когда я к нему обращаюсь.
Это тот же аргумент, почему Линус ненавидит C ++. Ваш код может удивить читателя. Он ненавидит перегрузку операторов:
a + b
это не обязательно означает простое добавление. Это может означать очень сложную операцию, как и свойства C #. Может иметь побочные эффекты. Он может делать что угодно.Честно говоря, я считаю это слабым аргументом. Оба языка полны подобных вещей. (Следует ли избегать перегрузки операторов и в C #? В конце концов, там можно использовать тот же аргумент)
Свойства допускают абстракцию. Мы можем притвориться , что что-то является обычным полем, и использовать это так, как если бы оно было одним, и не беспокоиться о том, что происходит за кулисами.
Обычно это считается хорошим, но очевидно, что программист должен писать значимые абстракции. Ваши свойства должны вести себя как поля. У них не должно быть побочных эффектов, они не должны выполнять дорогостоящих или небезопасных операций. Мы хотим думать о них как о полях.
Однако у меня есть еще одна причина считать их не идеальными. Их нельзя передавать по ссылке на другие функции.
Поля можно передавать как
ref
, что позволяет вызываемой функции напрямую обращаться к ним. Функции могут быть переданы как делегаты, что позволяет вызываемой функции напрямую обращаться к ним.Свойства ... не могу.
Это отстой.
Но это не значит, что свойства - зло или их нельзя использовать. Для многих целей они великолепны.
источник
ref
параметры; член (напримерFoo
) формыvoid Foo<T>(ActionByRef<Point,T> proc, ref T param)
со специальным атрибутом и преобразовать компиляторthing.Foo.X+=someVar;
вFoo((ref Point it, ref int param)=>it.X += param, ref someVar);
. Поскольку лямбда является статическим делегатом, закрытие не требуется, и пользователь объекта сможет использовать любое место хранения, поддерживающее свойство, как подлинныйref
параметр (и может передать его другим методам в качествеref
параметра).Еще в 2009 году этот совет выглядел просто как крик " Who Moved My Cheese". разновидности . Сегодня это почти до смешного устарело.
Один очень важный момент, который многие ответы, кажется, ходят на цыпочках, но не совсем затрагивают, заключается в том, что эти предполагаемые «опасности» свойств являются преднамеренной частью дизайна фреймворка!
Да, недвижимость может:
Укажите разные модификаторы доступа для геттера и сеттера. Это преимущество перед полями. Распространенным шаблоном является наличие общедоступного получателя и защищенного или внутреннего установщика, очень полезный метод наследования, который невозможно реализовать только с помощью полей.
Выбросить исключение. На сегодняшний день это остается одним из наиболее эффективных методов проверки, особенно при работе с UI-фреймворками, которые включают концепции привязки данных. Гораздо сложнее обеспечить, чтобы объект оставался в допустимом состоянии при работе с полями.
На выполнение потребуется много времени. Правильное сравнение здесь с методами , которые занимают столько же времени, а не с полями . Для утверждения «метод предпочтительнее» не дается никаких оснований, кроме личных предпочтений одного автора.
Возвращать разные значения из получателя при последующих выполнениях. Это почти похоже на шутку в такой непосредственной близости от точки, превозносящей достоинства параметров
ref
/out
с полями, чье значение поля послеref
/out
вызова почти гарантированно будет отличаться от его предыдущего значения, и это непредсказуемо.Если мы говорим о конкретном (и практически академическом) случае однопоточного доступа без афферентных связей, то вполне понятно, что иметь видимые побочные эффекты изменения состояния - это просто плохой дизайн свойств, и, возможно, моя память исчезает, но я просто не могу припомнить примеров, когда люди использовали
DateTime.Now
и ожидали, что одно и то же значение будет появляться каждый раз. По крайней мере, нет таких случаев, когда они бы так сильно не облажались с гипотетическимDateTime.Now()
.Вызывают наблюдаемые побочные эффекты - именно поэтому свойства были изначально изобретены как языковая функция. Собственные правила Microsoft Property Design указывают, что порядок установки не имеет значения, иначе это будет означать временную привязку . Конечно, вы не можете добиться временной привязки только с полями, но это только потому, что вы не можете вызвать какое-либо значимое поведение с одними только полями, пока не будет выполнен какой-либо метод.
Аксессоры свойств могут фактически помочь предотвратить определенные типы временной привязки, принудительно переводя объект в допустимое состояние до того, как будет предпринято какое-либо действие - например, если у класса есть a
StartDate
и anEndDate
, то установкаEndDate
доStartDate
может также принудительно выполнитьStartDate
обратное действие. Это верно даже в многопоточных или асинхронных средах, включая очевидный пример пользовательского интерфейса, управляемого событиями.Другие действия, которые могут выполнять свойства, которые не могут включать в себя:
Type
илиName
производных классов, может предоставить интересные, но, тем не менее, постоянные метаданные о самих себе.Item(i)
обращений, признает замечательную вещь.Рихтер явно плодовитый автор и много знает о CLR и C #, но я должен сказать, что похоже, когда он изначально написал этот совет (я не уверен, что он в его более поздних версиях - я искренне надеюсь, что нет) что он просто не хотел отказываться от старых привычек и испытывал проблемы с принятием соглашений C # (по сравнению, например, с C ++).
Я имею в виду, что его аргумент о том, что «свойства считаются вредными», по сути сводится к одному утверждению: свойства выглядят как поля, но они могут не действовать как поля. Проблема с этим утверждением в том, что оно не соответствует действительности или, в лучшем случае, сильно вводит в заблуждение. Свойства не выглядят как поля - по крайней мере, они не должны выглядеть как поля.
В C # есть два очень строгих соглашения о кодировании с аналогичными соглашениями, используемыми в других языках CLR, и FXCop будет кричать на вас, если вы не будете им следовать:
Таким образом, нет двусмысленности в том,
Foo.Bar = 42
является ли метод доступа к свойству или методом доступа к полю. Это средство доступа к свойству, и с ним следует обращаться как с любым другим методом - он может быть медленным, генерировать исключение и т. Д. Такова природа абстракции - как реагировать полностью на усмотрение объявляющего класса. Разработчики классов должны применять принцип наименьшего удивления, но вызывающие не должны предполагать ничего о свойстве, кроме того, что оно делает то, что написано на банке. Это специально.Альтернативой свойствам являются везде методы получения / установки. Это подход Java, и он с самого начала был неоднозначным . Ничего страшного, если это твоя сумка, но это не то, что мы делаем в лагере .NET. Мы пытаемся, по крайней мере, в рамках статически типизированной системы, избегать того, что Фаулер называет синтаксическим шумом . Нам не нужны лишние круглые скобки, лишние
get
/set
бородавки или дополнительные сигнатуры методов - если мы можем избежать их без потери ясности.Говорите, что хотите, но
foo.Bar.Baz = quux.Answers[42]
это всегда будет намного легче читатьfoo.getBar().setBaz(quux.getAnswers().getItem(42))
. И когда вы читаете тысячи строк этого текста в день, это имеет значение.(И если ваш естественный ответ на предыдущий абзац - «конечно, его трудно читать, но было бы проще, если бы вы разбили его на несколько строк», то я с сожалением должен сказать, что вы полностью упустили суть .)
источник
Я не вижу причин, по которым вы не должны использовать Properties вообще.
Автоматические свойства в C # 3+ лишь немного упрощают синтаксис (а-ля синтаксический сахар).
источник
Это всего лишь мнение одного человека. Я прочитал довольно много книг по C # и еще не видел, чтобы кто-нибудь еще говорил «не использовать свойства».
Я лично считаю, что свойства - одно из лучших свойств C #. Они позволяют вам раскрывать состояние с помощью любого механизма, который вам нравится. Вы можете лениво создать экземпляр, когда что-то используется в первый раз, и вы можете выполнить проверку при установке значения и т. Д. При их использовании и записи я просто думаю о свойствах как о установщиках и получателях, которые имеют гораздо более приятный синтаксис.
Что касается оговорок с недвижимостью, то здесь есть пара. Одно, вероятно, связано с неправильным использованием свойств, другое может быть незаметным.
Во-первых, свойства - это типы методов. Это может быть удивительно, если вы поместите в свойство сложную логику, потому что большинство пользователей класса ожидают, что свойство будет довольно легким.
Например
Я считаю, что использование методов для этих случаев помогает различать.
Во-вторых, что более важно, свойства могут иметь побочные эффекты, если вы оцениваете их во время отладки.
Допустим, у вас есть такая недвижимость:
Теперь предположим, что у вас есть исключение, которое возникает, когда делается слишком много запросов. Угадайте, что происходит, когда вы начинаете отладку и меняете свойство в отладчике? Плохие вещи. Избегайте этого! Просмотр свойства меняет состояние программы.
Это единственные предостережения, которые у меня есть. Я думаю, что преимущества собственности намного перевешивают проблемы.
источник
Причина должна быть дана в очень конкретном контексте. Обычно все наоборот - рекомендуется использовать свойства, поскольку они дают вам уровень абстракции, позволяющий изменять поведение класса, не затрагивая его клиентов ...
источник
Я не могу не обратить внимание на детали мнения Джеффри Рихтера:
Неправильно: поля могут быть помечены как доступные только для чтения, поэтому запись в них может осуществляться только конструктором объекта.
Неправильно: реализация класса может изменить модификатор доступа к полю с публичного на частный. Попытки прочитать закрытые поля во время выполнения всегда приводят к исключению.
источник
Я не согласен с Джеффри Рихтером, но могу догадаться, почему ему не нравится недвижимость (я не читал его книгу).
Несмотря на то, что свойства аналогичны методам (с точки зрения реализации), как пользователь класса я ожидаю, что его свойства будут вести себя «более или менее» как публичное поле, например:
К сожалению, я встречал свойства, которые так себя не вели. Но проблема не в самих свойствах, а в людях, которые их реализовали. Так что для этого просто нужно образование.
источник
Бывает время, когда я не использую свойства, и это при написании кода .Net Compact Framework. Компилятор CF JIT не выполняет ту же оптимизацию, что и компилятор JIT для настольных компьютеров, и не оптимизирует простые средства доступа к свойствам, поэтому в этом случае добавление простого свойства приводит к небольшому раздутию кода из-за использования общедоступного поля. Обычно это не проблема, но почти всегда в мире Compact Framework вы сталкиваетесь с жесткими ограничениями памяти, поэтому даже такая крошечная экономия имеет значение.
источник
Вы не должны избегать их использования, но вы должны использовать их квалифицированно и осторожно по причинам, указанным другими участниками.
Однажды я увидел свойство, называемое чем-то вроде «Клиенты», которое внутренне открыло внепроцессный вызов базы данных и прочитало список клиентов. В клиентском коде был код for (int i to Customers.Count), который вызывал отдельный вызов базы данных на каждой итерации и для доступа выбранного клиента. Это вопиющий пример, демонстрирующий принцип очень легкого использования свойства - редко больше, чем доступ к внутреннему полю.
Одним из аргументов ЗА использование свойств является то, что они позволяют проверить установленное значение. Другой заключается в том, что значением свойства может быть производное значение, а не отдельное поле, например TotalValue = количество * количество.
источник
Лично я использую свойства только при создании простых методов получения / установки. Когда я перехожу к сложным структурам данных, я отхожу от этого.
источник
Аргумент предполагает, что свойства плохие, потому что они выглядят как поля, но могут выполнять неожиданные действия. Это предположение опровергается ожиданиями .NET-программистов:
Свойства не похожи на поля. Поля выглядят как свойства.
Итак, поле похоже на свойство, которое гарантированно никогда не вызовет исключения.
Итак, поле похоже на свойство, но у него есть дополнительные возможности: переход к
ref
/out
принимающим методам.Итак, поле похоже на быстрое свойство.
Итак, поле похоже на свойство, которое гарантированно возвращает одно и то же значение, если для поля не задано другое значение.
Опять же, поле - это свойство, которое гарантированно этого не делает.
Это может показаться удивительным, но не потому, что это делает свойство. Скорее всего, почти никто не возвращает изменяемые копии в свойствах, так что случай 0,1% вызывает удивление.
источник
Вызов методов вместо свойств значительно снижает удобочитаемость вызывающего кода. Например, в J # использование ADO.NET было кошмаром, потому что Java не поддерживает свойства и индексаторы (которые по сути являются свойствами с аргументами). Полученный код был крайне уродливым, с повсюду вызовами методов в круглых скобках.
Поддержка свойств и индексаторов - одно из основных преимуществ C # над Java.
источник