Właściwości

Adam Boduch

Właściwości to element związany z Programowaniem obiektowym. Właściwości, podobnie jak pola (te pojęcia są często ze sobą mylone) służą do gromadzenia danych (czyli do odczytywania oraz przypisywania informacji). Oprócz tego w przypadku właściwości istnieje możliwość zaprogramowania dodatkowych czynności podczas, na przykład, przypisywania wartości (danych) owej właściwości.

Właściwości są deklarowane z użyciem słowa kluczowego property. Właściwość musi posiadać przede wszystkim nazwę oraz typ (Integer, String itd.). Dodatkowo, właściwość może być opisana słowami kluczowymi read, write, default czy nodefault. Oto przykładowa deklaracja właściwości:

type
  TMyClass = class
  private
    FText : String;
  public
    property Text : String read FText;
  end;

Zacznijmy od tego, że właściwości mogą być zarówno do zapisu, jak i do odczytu; mogą jednakże być tylko do odczytu. W powyższym przykładzie właściwość Text jest właściwością tylko do odczytu (ma klauzulę read), a swoją zawartość pobiera z pola FText.

Właściwość Text jest w tym przypadku czymś na wzór łącznika pomiędzy użytkownikiem klasy a właściwością FText.

Przy próbie przypisania danych właściwości tylko do odczytu Delphi wyświetli komunikat o błędzie: Cannot assign to a read-only property.

Istnieje możliwość zaprogramowania dodatkowych czynności związanych z przypisaniem lub odczytywaniem danych z właściwości:

type
  TMyClass = class
  private
    FText : String;
    procedure SetText(Value : String);
  public
    property Text : String read FText write SetText;
  end;

W tym momencie przypisanie danych do właściwości Text jest równoznaczne z wywołaniem metody SetText:

var
  MyClass : TMyClass;
begin
  { konstruktor }
  MyClass.Text := 'Wartość';
  { destruktor }
end;

Kod ten jest równoznaczny z:

MyClass.SetText('Wartość');

Wspomniana procedura SetText musi zawsze posiadać parametr, który jest tego samego typu co właściwość. W tym przypadku właściwość Text jest typu String, tak więc parametr procedury SetText również musi być typu String. Skoro skorzystaliśmy z funkcji, możemy także użyć funkcji do odczytania wartości właściwości:

type
  TMyClass = class
  private
    FText : String;
    procedure SetText(Value : String);
    function GetText : String;
  public
    property Text : String read GetText write SetText;
  end;

Funkcja GetText będzie wywoływana w momencie żądania użytkownika o odczytanie zawartości Text. Funkcja GetText musi również zwracać typ String.

W przypadku właściwości stosuje się specjalną konwencję nazewnictwa. Nazwa funkcji i procedury jest tworzona od nazwy właściwości. Jedyną różnicą jest dodanie przedrostków Set oraz Get. I tak w przypadku właściwości Text mamy funkcję GetText oraz procedurę SetText. Nie jest to oczywiście obowiązek, ale taka reguła została przyjęta przez ogół programistów.

Procedura zastosowana w połączeniu z właściwością może być wykorzystana do różnych celów. W ciele takiej procedury mogą znaleźć się inne instrukcje, które ? przykładowo ? mają zostać wykonane w momencie przypisania danych do właściwości. W tej materii istnieje kilka reguł, do których trzeba się stosować:
*Funkcje i procedury użyte w połączeniu z właściwością muszą być zadeklarowane w tej samej klasie.
*Niezależnie od tego, czy jest to pole, funkcja czy procedura ? wszystkie one muszą być tego samego typu lub mieć te same parametry.
*Metody połączone z właściwościami nie mogą być opatrzone klauzulą Virtual lub Dynamic ? nie mogą być dziedziczone ani przeładowane.

Właściwości bardzo często są wykorzystywane podczas tworzenia nowych komponentów. VCL jest biblioteką skalowaną, tzn. ma możliwość łatwego rozbudowywania. Na podstawie istniejących już klas lub kontrolek można budować nowe. W takim przypadku, używając klasy, można użyć jeszcze jednej sekcji, o której dotychczas nie wspominałem ? Published. W sekcji Published w klasie znajdują się przeważnie właściwości, które będą dostępne dla inspektora obiektów, bowiem tworzenie nowego komponentu w dużej mierze opiera się na tworzeniu odpowiedniej klasy.

Wartości domyślne

Wspominałem na samym początku, iż właściwości mogą mieć klauzule Default lub Nodefault. Służą one po prostu nadawaniu domyślnych wartości danym właściwościom.

Oto przykład nadania wartości domyślnej właściwości Count:

property Count : Integer read FCount write FCount default 1;

W takim przypadku domyślna wartość właściwości Count to 1.

Klauzula default obejmuje jedynie typy liczbowe i zbiory (Set) ? nie obsługuje natomiast ciągów znakowych String.

W przypadku typów typu Boolean używa się klauzuli Stored, a nie Default, np.:

property DoIt : Boolean read FDoIt write FDoIt stored False;

Jeśli nie skorzystamy z klauzuli Stored, program jako domyślną wartość właściwości przyjmie True.

Klauzula Nodefault jest przeciwieństwem Default ? oznacza, że właściwość nie będzie miała wartości domyślnej. Klauzula ta jest stosowana jedynie w niektórych przypadkach, gdy klasa bazowa, z której korzysta dana klasa, ma właściwość domyślną.

TBaseClass = class
private
  FProp : Integer;
published
  property Prop : Integer read FProp write FProp default 1;
end;

TMyClass = class(TBaseClass)
private
  FProp : Integer;
published
  property Prop : Integer read FProp write FProp nodefault;
end;

W takim przypadku klasa TMyClass także będzie mieć właściwość Prop, tyle że nie będzie już ona zawierała wartości domyślnej.

Zobacz też:

5 komentarzy

Zaciekawiło mnie to: "Wspominałem na samym początku, iż właściwości mogą mieć klauzule default lub nodefault. Służą one po prostu nadawaniu domyślnych wartości danym właściwościom."

Default/NoDefault nijak mają się do inicjowania wartości domyślnej, wystarczy poczytać helpa by dowiedzieć się że służą one do sterowania czy wartość property zostanie zapisana do dfm.

Świetny artykuł

wlasnie przeczytalem atrukul "Property overrides and redeclarations" i w tym artukule brak informacji o tym. Ja osobiscie przeczytalem arykul ale nie rozumiem kiedy przedefiniowanie wlasciwosci sie przydaje bo ja nie widze roznicy w dzialaniu pomiedzy

property Value: integer read GetValue write SetValue;
a
property Value read GetValue write SetValue;

gdyz i tak musze castowac to danego obiektu aby otrzymac zadane wlasciwosci... eh lepiej sami zobaczcie :P

http://info.borland.com/techpubs/delphi/delphi5/oplg/classes.html#19292

@Deti:
Kiepski przykład, samo 'default' stosuje się przy właściwościach tablicowych...

Warto też dodać bardzo ciekawą rzecz: mianowicie taki przykład:

property Count : Integer read FCount write FCount default;

Będzie oznaczało, że wywołując gdziekolwiek klasę, w której toto jest - automatycznie będzie wskazywać na właściwość Count - tak jest na przykład w TMemo: mamy Memo.Lines.Strings[5] = Memo.Lines[5].

Właściwość Text jest w tym przypadku czymś na wzór łącznika pomiędzy użytkownikiem klasy a właściwością FText.

Czy nie powinno być "polem FText" ? .. FText nie jest właściwością.

Artykuł bardzo dobry! - w tygodniu dopisze jeszcze o używaniu właściwości tablicowych i innych rzeczy...