Nieruchomość (oprogramowanie) - Property (programming)

Nieruchomość , w niektórych obiektowych języków programowania , to specjalny rodzaj klasy członka, średnio w funkcjonalności pomiędzy pola (lub członka danych) i metody . Składnia odczytywania i zapisywania właściwości jest podobna do pól, ale odczyty i zapisy właściwości są (zazwyczaj) tłumaczone na wywołania metod pobierających i ustawiających . Składnia podobna do pól jest łatwiejsza do odczytania i zapisania niż wiele wywołań metod, jednak wstawienie wywołań metod „pod maską” pozwala na walidację danych , aktywną aktualizację (np. elementów GUI ) lub implementację tego, co można nazwać „ pola tylko do odczytu ".

Zobacz pouczający przykład dla języka C# poniżej.

Wsparcie w językach

Języki programowania obsługujące właściwości obejmują ActionScript 3 , C# , D , Delphi / Free Pascal , eC , F# , Kotlin , JavaScript , Objective-C 2.0 , Python , Scala , Swift , Lua i Visual Basic .

Niektóre języki obiektowe, takie jak Java i C ++ , nie obsługują właściwości i wymagają programista zdefiniować parę dostępowych i mutator metod zamiast.

Oberon-2 zapewnia alternatywny mechanizm wykorzystujący flagi widoczności obiektów.

Inne języki zaprojektowane dla wirtualnej maszyny Java , takie jak Groovy , natywnie obsługują właściwości.

Chociaż C++ nie ma właściwości pierwszej klasy, można je emulować z powodu przeciążenia operatora .

Należy również zauważyć, że niektóre kompilatory C++ obsługują właściwości pierwszej klasy (na przykład kompilator Microsoft C++).

W większości języków właściwości są implementowane jako para metod akcesora/mutatora, ale są dostępne przy użyciu tej samej składni, co w przypadku pól publicznych. Pominięcie metody z pary daje właściwość tylko do odczytu lub rzadko spotykaną właściwość tylko do zapisu .

W niektórych językach bez wbudowanej obsługi właściwości podobną konstrukcję można zaimplementować jako pojedynczą metodę, która zwraca lub zmienia dane bazowe, w zależności od kontekstu wywołania. Takie techniki są używane np. w Perlu .

Niektóre języki (Ruby, Smalltalk) osiągają składnię podobną do właściwości przy użyciu normalnych metod, czasami z ograniczoną ilością cukru składniowego .

Warianty składni

Niektóre języki stosują dobrze ustalone konwencje składni dotyczące formalnego określania i wykorzystywania właściwości i metod.

Wśród tych konwencji:

  • Notacja kropkowa
  • Notacja nawiasowa

Notacja kropkowa

Poniższy przykład demonstruje notację kropkową w JavaScript.

document.createElement('pre');

Notacja nawiasowa

Poniższy przykład ilustruje notację nawiasów w JavaScript.

document['createElement']('pre');

Przykładowa składnia

C#

class Pen 
{
    private int color; // private field
    
    // public property
    public int Color 
    {  
        get
        {
            return this.color;
        }
        set 
        {
            if (value > 0) {
                this.color = value;
            }
        }
    }
}
// accessing:
Pen pen = new Pen();
int color_tmp = 0;
// ...
pen.Color = 17;
color_tmp = pen.Color;
// ...
pen.Color = ~pen.Color; // bitwise complement ...

// another silly example:
pen.Color += 1; // a lot clearer than "pen.set_Color(pen.get_Color() + 1)"!

Najnowsze wersje C# umożliwiają również "właściwości implementowane automatycznie", w których pole zapasowe właściwości jest generowane przez kompilator podczas kompilacji. Oznacza to, że nieruchomość musi mieć ustawiacz. Może być jednak prywatny.

class Shape 
{
    
    public Int32 Height { get; set; }
    public Int32 Width  { get; private set; }
    
}

C++

C++ nie posiada właściwości pierwszej klasy, ale istnieje kilka sposobów na emulację właściwości w ograniczonym stopniu. Dwa z nich są następujące:

#include <iostream>

template <typename T> class property {
        T value;
    public:
        T & operator = (const T &i) {
            return value = i;
        }
        // This template class member function template serves the purpose to make
        // typing more strict. Assignment to this is only possible with exact identical types.
        // The reason why it will cause an error is temporary variable created while implicit type conversion in reference initialization.
        template <typename T2> T2 & operator = (const T2 &i) {
            T2 &guard = value;
            throw guard; // Never reached.
        }

        // Implicit conversion back to T. 
        operator T const & () const {
            return value;
        }
};

struct Foo {
    // Properties using unnamed classes.
    class {
            int value;
        public:
            int & operator = (const int &i) { return value = i; }
            operator int () const { return value; }
    } alpha;

    class {
            float value;
        public:
            float & operator = (const float &f) { return value = f; }
            operator float () const { return value; }
    } bravo;
};

struct Bar {
    // Using the property<>-template.
    property <bool> alpha;
    property <unsigned int> bravo;
};

int main () {
    Foo foo;
    foo.alpha = 5;
    foo.bravo = 5.132f;

    Bar bar;
    bar.alpha = true;
    bar.bravo = true; // This line will yield a compile time error
                      // due to the guard template member function.
    ::std::cout << foo.alpha << ", "
                << foo.bravo << ", "
                << bar.alpha << ", "
                << bar.bravo
                << ::std::endl;
    return 0;
}

Specyficzne dla C++, Microsoft i C++Builder

Przykład zaczerpnięty ze strony dokumentacji MSDN .

// declspec_property.cpp
struct S
{
   int i;
   void putprop(int j)
   { 
      i = j;
   }

   int getprop()
   {
      return i;
   }

   __declspec(property(get = getprop, put = putprop)) int the_prop;
};

int main()
{
   S s;
   s.the_prop = 5;
   return s.the_prop;
}

D

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; // bitwise complement

// the set property can also be used in expressions, just like regular assignment
int theColor = (pen.color = 0xFF0000);

W wersji D 2 każdy akcesor lub mutator właściwości musi być oznaczony @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;
    }
}

Delphi/Free Pascal

type TPen = class
  private
    FColor: TColor;
    function GetColor: TColor;
    procedure SetColor(const AValue: TColor);
  public
    property Color: Integer read GetColor write SetColor;
end;

function TPen.GetColor: TColor;
begin
  Result := FColor;
end;

procedure TPen.SetColor(const AValue: TColor);
begin
  if FColor <> AValue
   then FColor := AValue;
end;
// accessing:
var Pen: TPen;
// ...
Pen.Color := not Pen.Color;

(*
Delphi also supports a 'direct field' syntax -

property Color: TColor read FColor write SetColor;

or

property Color: TColor read GetColor write FColor;

where the compiler generates the exact same code as for reading and writing
a field. This offers the efficiency of a field, with the safety of a property.
(You can't get a pointer to the property, and you can always replace the member
access with a method call.)
*)

eK

class Pen 
{
   // private data member
   Color color;
public:
   // public property
   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 } };

F#

type Pen() = class
    let mutable _color = 0

    member this.Color
        with get() = _color
        and set value = _color <- value
end
let pen = new Pen()
pen.Color <- ~~~pen.Color

JavaScript

function Pen() {
    this._color = 0;
}
// Add the property to the Pen type itself, can also
// be set on the instance individually
Object.defineProperties(Pen.prototype, {
    color: {
        get: function () {
            return this._color;
        },
        set: function (value) {
            this._color = value;
        }
    }
});
var pen = new Pen();
pen.color = ~pen.color; // bitwise complement
pen.color += 1; // Add one

ActionScript 3.0

package  {
	public class Pen {
		private var _color:uint = 0;
		
		public function get color():uint {
			return _color;
		}
		
		public function set color(value:uint):void {
			_color = value;
		}
	}
}
var pen:Pen = new Pen();
pen.color = ~pen.color; // bitwise complement
pen.color += 1; // add one

Cel-C 2.0

@interface Pen : NSObject
@property (copy) NSColor *colour;	// The "copy" attribute causes the object's copy to be
					// retained, instead of the original.
@end

@implementation Pen
@synthesize colour;			// Compiler directive to synthesise accessor methods.
					// It can be left behind in Xcode 4.5 and later.
@end

Powyższy przykład może być użyty w dowolnej metodzie takiej jak ta:

Pen *pen = [[Pen alloc] init];
pen.colour = [NSColor blackColor];
float red = pen.colour.redComponent;
[pen.colour drawSwatchInRect: NSMakeRect(0, 0, 100, 100)];

PHP

class Pen
{
    private int $color = 1;

    function __set($property, $value)
    {
        if (property_exists($this, $property)) { 
            $this->$property = $value;
        }
    }

    function __get($property)
    {
        if (property_exists($this, $property)) {
            return $this->$property;
        }
        return null;
    }
}
$p = new Pen();
$p->color = ~$p->color; // Bitwise complement
echo $p->color;

Pyton

Właściwości działają poprawnie tylko dla klas w nowym stylu (klas, które mają objectjako nadklasę ) i są dostępne tylko w Pythonie 2.2 i nowszych (zobacz odpowiednią sekcję samouczka Ujednolicenie typów i klas w Pythonie 2.2 ). Python 2.6 dodał nową składnię obejmującą dekoratory do definiowania właściwości.

class Pen:
    def __init__(self) -> None:
        self._color = 0  # "private" variable

    @property
    def color(self):
        return self._color

    @color.setter
    def color(self, color):
        self._color = color
pen = Pen()
# Accessing:
pen.color = ~pen.color  # Bitwise complement ...

Rubin

class Pen
  def initialize
    @color = 0
  end
    
  # Defines a getter for the @color field
  def color
    @color
  end

  # Defines a setter for the @color field
  def color=(value)
    @color = value
  end
end

pen = Pen.new
pen.color = ~pen.color    # Bitwise complement

Ruby zapewnia również automatyczne syntezatory pobierające/ustawiające zdefiniowane jako metody instancji klasy.

class Pen
  attr_reader :brand    # Generates a getter for @brand (Read-Only)
  attr_writer :size     # Generates a setter for @size  (Write-Only)
  attr_accessor :color  # Generates both a getter and setter for @color (Read/Write)

  def initialize
    @color = 0          # Within the object, we can access the instance variable directly
    @brand = "Penbrand"
    @size = 0.7         # But we could also use the setter method defined by the attr_accessor Class instance method
  end
end

pen = Pen.new
puts pen.brand           # Accesses the pen brand through the generated getter
pen.size = 0.5           # Updates the size field of the pen through the generated setter
pen.color = ~pen.color

Visual Basic

Visual Basic (.NET 2003-2010)

Public Class Pen
 
    Private _color As Integer ' Private field

    Public Property Color() As Integer ' Public property
        Get
            Return _color
        End Get
        Set(ByVal value As Integer)
            _color = value
        End Set
    End Property

End Class
' Create Pen class instance
Dim pen As New Pen()

' Set value
pen.Color = 1

' Get value
Dim color As Int32 = pen.Color

Visual Basic (tylko .NET 2010)

Public Class Pen

    Public Property Color() As Integer ' Public property

End Class
' Create Pen class instance
Dim pen As New Pen()

' Set value
pen.Color = 1

' Get value
Dim color As Int32 = pen.Color

Visual Basic 6

' in a class named clsPen
Private m_Color As Long

Public Property Get Color() As Long
    Color = m_Color
End Property

Public Property Let Color(ByVal RHS As Long)
    m_Color = RHS
End Property
' accessing:
Dim pen As New clsPen
' ...
pen.Color = Not pen.Color

Zobacz też