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