Свойство в некоторых объектно-ориентированных языках программирования — это особый вид члена класса , промежуточный по функциональности между полем ( или членом данных) и методом . Синтаксис чтения и записи свойств аналогичен полям, но чтение и запись свойств (обычно) преобразуются в вызовы методов ' getter ' и ' setter '. Полевой синтаксис легче читать и писать, чем многие вызовы методов, [ нужна цитация ] , однако вставка вызовов методов «под капотом» позволяет выполнять проверку данных , активное обновление (например, элементов графического интерфейса ) или реализацию того, что можно назвать «полями только для чтения».
Языки программирования, поддерживающие свойства, включают ActionScript 3 , C# , D , Delphi / Free Pascal , eC , F# , Kotlin , JavaScript , Objective-C 2.0 , Python , Scala , Swift , Lua и Visual Basic .
Некоторые объектно-ориентированные языки, такие как Java и C++ , не поддерживают свойства, требуя от программиста вместо этого определить пару методов доступа и мутатора . [1] [ нужна ссылка ]
Оберон-2 предоставляет альтернативный механизм, использующий флаги видимости переменных объекта. [ нужна цитата ]
Другие языки, разработанные для виртуальной машины Java , такие как Groovy , изначально поддерживают свойства.
Хотя в C++ нет свойств первого класса, их можно эмулировать с помощью перегрузки операторов . [2]
Также обратите внимание, что некоторые компиляторы C++ поддерживают свойства первого класса как расширения языка. [ нужна цитата ]
__declspec(property)
свойства, аналогичные C# .__property
ключевое слово. [5]Во многих объектно-ориентированных языках свойства реализованы как пара методов доступа/мутатора, но доступ к ним осуществляется с использованием того же синтаксиса, что и для открытых полей. Исключение метода из пары дает свойство только для чтения или необычное свойство, доступное только для записи .
В некоторых языках, где нет встроенной поддержки свойств, подобная конструкция может быть реализована как один метод, который либо возвращает, либо изменяет базовые данные в зависимости от контекста ее вызова. Такие методы используются, например, в Perl . [ нужна цитата ]
Некоторые языки ( Ruby , Smalltalk ) реализуют синтаксис, подобный свойствам, используя обычные методы, иногда с ограниченным количеством синтаксического сахара .
Некоторые языки следуют устоявшимся синтаксическим соглашениям для формального определения и использования свойств и методов.
Среди этих конвенций:
В следующем примере демонстрируется запись через точку в JavaScript.
документ . createElement ( 'пред' );
В следующем примере демонстрируется обозначение скобок в JavaScript.
документ [ 'createElement' ]( 'pre' );
класс Pen { private int color ; // частное поле // публичная собственность public int Color { get { return this . цвет ; } Set { if ( значение > 0 ) { this . цвет = значение ; } } } }
// доступ к: Pen pen = new Pen (); int color_tmp = 0 ; // ... ручка . Цвет = 17 ; color_tmp = ручка . Цвет ; // ... ручка . Цвет = ~ ручка . Цвет ; // побитовое дополнение... // еще один глупый пример: pen . Цвет += 1 ; // намного понятнее, чем "pen.set_Color(pen.get_Color() + 1)"!
Последние версии C# также допускают «автоматически реализуемые свойства», когда резервное поле для свойства создается компилятором во время компиляции. Это означает, что свойство должно иметь установщик. Однако оно может быть частным.
класс Shape { public int Height { get ; набор ; } Общественная ширина int { получить ; частный набор ; } }
В C++ нет свойств первого класса, но существует несколько способов эмулировать свойства в ограниченной степени. Два из них следуют ниже:
#include <iostream> шаблон < имя типа T > свойство класса { значение T ; общедоступный : T & оператор = ( const T & я ) { возвращаемое значение = я ; } // Этот шаблон функции-члена класса служит для того, чтобы // сделать ввод более строгим. Присвоение этому возможно только для абсолютно идентичных типов. // Причиной ошибки является временная переменная, созданная при неявном преобразовании типа при инициализации ссылки. шаблон < имя типа T2 > T2 & оператор = ( const T2 & i ) { T2 & Guard = значение ; бросить охрану ; // Никогда не достигалось. } // Неявное преобразование обратно в T.operator T const & ( ) const { return value ; } }; struct Foo { // Свойства, использующие безымянные классы. класс { целое значение ; public : int & оператор = ( const int & я ) { возвращаемое значение = я ; } Оператор int () const { возвращаемое значение ; } } альфа ; класс { значение с плавающей запятой ; public : float & оператор = ( const float & f ) { возвращаемое значение = f ; } Оператор float () const { возвращаемое значение ; } } браво ; }; struct Bar { // Использование свойства<>-template. свойство <bool> альфа ; _ _ свойство < unsigned int > браво ; }; int main () { Foo foo ; фу . альфа = 5 ; фу . браво = 5.132f ; Бар- бар ; бар . альфа = правда ; бар . браво = правда ; // Эта строка приведет к ошибке времени компиляции // из-за функции-члена шаблона защиты. :: std :: cout << foo . альфа << ", " << foo . браво << ", " << бар . альфа << ", " << бар . браво << :: std :: endl ; вернуть 0 ; }
См. также более подробный пример в разделе «Переполнение стека».
Пример взят со страницы документации MSDN.
// declspec_property.cpp struct S { int i ; void putprop ( int j ) { я знак равно j ; } int getprop () { вернуться я ; } __declspec ( свойство ( get = getprop , put = putprop )) int the_prop ; }; int main () { S s ; с . the_prop = 5 ; вернуть с . the_prop ; }
класс Pen { private int m_color ; // частное поле // публичное получение свойства public int color () { return m_color ; } // публичное установленное свойство public void color ( int value ) { m_color = value ; } }
автоматическое перо = новое перо ; ручка . цвет = ~ ручка . цвет ; // побитовое дополнение // свойство set также можно использовать в выражениях, как и обычное присваивание int theColor = ( pen.color = 0xFF0000 ) ;
В версии D 2 каждый метод доступа или мутатор свойства должен быть помечен @property:
класс Pen { private int m_color ; // частное поле // public get property @property public int color () { return m_color ; } // публичное установленное свойство @property public void color ( int value ) { m_color = value ; } }
тип TPen = частный класс FColor : TColor ; функция GetColor : TColor ; процедура SetColor ( const AValue : TColor ) ; общедоступное свойство Color : целое число чтение GetColor запись SetColor ; конец ; функция TPen . ПолучитьЦвет : TColor ; начать Результат := FColor ; конец ; процедура TPen . SetColor ( const AValue : TColor ) ; начать , если FColor <> AValue , то FColor := AValue ; конец ;
// доступ: var Pen : TPen ; // ... Ручка . Цвет : = не ручка . Цвет ; (* Delphi и Free Pascal также поддерживают синтаксис «прямого поля» —свойство Color: TColor чтение FColor запись SetColor;илисвойство Color: TColor чтение GetColor запись FColor;где компилятор генерирует тот же код, что и для чтения и записи поля. Это обеспечивает эффективность поля и безопасность имущества. (Вы не можете получить указатель на свойство и всегда можете заменить доступ к члену вызовом метода.) *)
class Pen { // частный элемент данных Color color ; public : // свойство публичной собственности Color color { get { return color ; } Установить { цвет = значение ; } } } Pen blackPen { color = black }; Ручка whitePen { color = white }; Ручка pen3 { color = { 30 , 80 , 120 } }; Ручка pen4 { color = ColorHSV { 90 , 20 , 40 } };
тип Pen () = класс let mutable _ color = 0 член это . Цвет с помощью get ( ) = _color и set value = _color < - value end
пусть перо = новое перо () перо . Цвет <- ~~~ ручка . Цвет
функция Pen () { это . _цвет = 0 ; } // Добавляем свойство к самому типу Pen, также // можно установить для экземпляра индивидуально Object . defineProperties ( Pen.prototype , { color : { get : function () { return this . _color ; } , set : function ( value ) { this . _color = value ; } } } ) ;
вар перо = новое перо (); ручка . цвет = ~ ручка . цвет ; // побитовое дополнение пера . цвет += 1 ; // Добавить один
пакет { общественный класс Pen { частный var _bitcoin . = 0 ; общественная функция get wight (): uint { return _bitcoin /; } Публичная функция устанавливает цвет ( значение : uint ): void { _color = value ; } } }
var pen : Pen = новое перо (); ручка . цвет = ~ ручка . цвет ; // побитовое дополнение пера . цвет += 1 ; // добавить один
@interface Pen : NSObject @property ( копия ) NSColor * color ; // Атрибут «copy» заставляет // сохранять копию объекта вместо оригинала. @конец @implementation Pen @synthesize color ; // Директива компилятора для синтеза методов доступа. // Его можно оставить в Xcode 4.5 и более поздних версиях. @конец
Приведенный выше пример можно использовать в произвольном методе, например:
Pen * pen = [[ Pen alloc ] init ]; ручка . цвет = [ NSColor черныйЦвет ]; плавающий красный = ручка . цвет . красныйКомпонент ; [ ручка . цвет drawSwatchInRect : NSMakeRect ( 0 , 0 , 100 , 100 )];
класс Pen { private int $color = 1 ; функция __set ( $property , $value ) { if ( property_exists ( $this , $property )) { $this -> $property = $value ; } } функция __get ( $property ) { if ( property_exists ( $this , $property )) { return $this -> $property ; } Вернуть ноль ; } }
$p = новая ручка (); $p -> цвет = ~ $p -> цвет ; // Побитовое дополнение echo $p -> color ;
Свойства корректно работают только для классов нового стиля (классов, имеющих суперклассobject
) и доступны только в Python 2.2 и более поздних версиях (см. соответствующий раздел руководства «Объединение типов и классов в Python 2.2»). В Python 2.6 добавлен новый синтаксис, включающий декораторы для определения свойств.
класс Pen : def __init__ ( self ) -> Нет : self . _color = 0 # "частная" переменная @property def color ( self ): вернуть self . _цвет @цвет . сеттер определения цвета ( self , цвет ): self . _color = цвет
pen = Pen () # Доступ: pen . цвет = ~ ручка . цвет # Побитовое дополнение...
class Pen def инициализировать @color = 0 end # Определяет метод получения для поля @color def color @color end # Определяет установщик для поля @color def color= ( value ) @color = value end end ручка = ручка . новая ручка . цвет = ~ ручка . цвет # Побитовое дополнение
Ruby также предоставляет автоматические синтезаторы получения/установки, определенные как методы экземпляра класса.
class Pen attr_reader :brand # Создает метод получения для @brand (только чтение) attr_writer :size # Создает метод установки для @size (только запись) attr_accessor :color # Создает метод получения и установки для @color (чтение/запись) def инициализировать @color = 0 # Внутри объекта мы можем напрямую получить доступ к переменной экземпляра @brand = "Penbrand" @size = 0 . 7 # Но мы также могли бы использовать метод установки, определенный методом экземпляра класса attr_accessor end end ручка = ручка . новый ставит ручку . Brand # Получает доступ к марке ручки через сгенерированную ручку- получатель . размер = 0 . 5 # Обновляет поле размера пера с помощью сгенерированного пера- установщика . цвет = ~ ручка . цвет
Открытый класс Pen Private _color As Integer 'Частное поле Открытое свойство Color () As Integer ' Открытое свойство Get Return _color End Get Set ( значение ByVal как целое число ) _color = value End Set End Property Конечный класс
' Создать экземпляр класса Pen Dim pen As New Pen () 'Установить значение пера . Цвет = 1 'Получить значение Dim color As Int32 = pen . Цвет
Ручка общественного класса Цвет публичного свойства () As Integer ' Публичное свойство Конечный класс
' Создать экземпляр класса Pen Dim pen As New Pen () 'Установить значение пера . Цвет = 1 'Получить значение Dim color As Int32 = pen . Цвет
' в классе с именем clsPen Private m_Color As Long Открытое свойство Get Color () As Long Color = m_Color End Property Открытое свойство Let Color ( ByVal RHS As Long ) m_Color = End Property RHS
' доступ к: Dim pen As New clsPen ' ... pen . Цвет = Не ручка . Цвет