Operator "as" Delphi

0

Jaką funkcje pełni operator "as"? Jest to rzutowanie?

7

Rzutowanie referencji na konkretny typ klasy lub interfejsu. Użycie operatora as od zwykłego (twardego) rzutowania odróżnia to, że wykonywane jest dodatkowe sprawdzenie zgodności typów i jeśli rzutowanie nie może być wykonane, albo kompilacja jest przerywana (compile time), albo emitowany jest odpowiedniej klasy wyjątek (runtime).

0

Jedno pytanie, w przypadku as możemy rzutować tylko klasy które są dziedziczone, tzn te klasy które mają zachowana hierarchię rodzic, potomek ?

4

No tak, klasy muszą być ze sobą powiązane. Jeśli kompilator wyczai, że powiązania nie ma, to przerwie pracę i zwróci błąd:

type
  TOne = class(TObject);
  TTwo = class(TObject);

type
  TFoo = class(TOne);
  TBar = class(TTwo);

var
  Foo: TFoo;
  Bar: TBar;
begin
  Bar := Foo as TBar; // Error: Class or Object types "TFoo" and "TBar" are not related
end.

To w przypadku FPC, ale Delphi nie używam, więc nie sprawdzę. W przypadku twardego rzutowania, błędu kompilacji nie będzie, ale i tak kompilator ostrzeże nas, że te klasy nie są powiązane i operacje na źle zrzutowanym obiekcie mogą spowodować wyjątek:

Bar := TBar(Foo); // Warning: Class types "TFoo" and "TBar" are not related

Samo rzutowanie referencji, nieważne czy twarde czy z dodatkowym testem zgodności, finalnie sprowadza się jedynie do przepisania adresu z jednej zmiennej do drugiej, a to zawsze zostanie wykonane prawidłowo. Jednak kompilator stara się pomóc, dlatego w przypadku braku powiązań albo tylko ostrzega o potencjalnych problemach, albo nie pozwala w ogóle skompilować kodu.

0

Rozumiem czym jest rzutowanie standardowe, ale nie rozumiem po co stosuje się rzutowanie po klasach, mógłby ktoś mi to wytłumaczyć?

3

Po to, aby zapewnić sobie dostęp do konkretnych składowych klasy. Bywa tak, że dostajemy referencję ogólną, np. TStream, która dostarcza wspólną funkcjonalność dla wszystkich klas pochodnych, np. metodę ReadBuffer. Możemy z niej skorzystać bez względu na to, czy w parametrze dostarczono obiekt TMemoryStream, TFileStream czy TStringStream — wszystkie te klasy posiadają tę metodę. Jeśli jednak będziemy chcieli uzyskać dostęp do metody, która nie istnieje w klasie TStream, ale istnieje np. w TFileStream, to referencję musimy najpierw zrzutować na TFileStream.


Już same zdarzenia są odpowiedzią na Twoje pytanie. One dostarczają obiekty Sender typu TObject, czyli rodzica wszystkich obiektów (a raczej klas). Żeby móc wykorzystać tę referencję, która de facto zawsze jest referencją komponentu, należy ją w jakiś sposób zrzutować na typ konkretnej klasy. I tutaj mamy trzy możliwości wykonania takiego rzutowania.

1. Rzutowanie ”twarde” – bez testowania powiązań:

procedure TMainForm.SomeButtonClick(Sender: TObject);
var
  Button: TButton;
begin
  Button := TButton(Sender);


2. Rzutowanie ”miękkie” — z testowaniem powiązań:

procedure TMainForm.SomeButtonClick(Sender: TObject);
var
  Button: TButton;
begin
  Button := Sender as TButton;


3. Deklaracja zmiennej pod adresem parametru — brak testowania czegokolwiek, jebnie to jebnie:

procedure TMainForm.SomeButtonClick(Sender: TObject);
var
  Button: TButton absolute Sender;


Każdy z tych sposobów ma swoje konkretne zastosowanie, a ich wykorzystanie zależy od potrzeby i często od grymasu programisty. Szczególnie, jeśli chodzi o dwa pierwsze przykłady — można skorzystać ze zmiennej pomocniczej, a można obejść się bez niej, obejmując konstrukcję z as w nawiasy.

Zapis z nawiasami jest wykorzystywany głównie wtedy, gdy na referencji wykonuje się tylko jedną czynność, np. przypisanie wartości do właściwości czy wywołanie jednej metody w obrębie danego bloku kodu. Ale często spotyka się też rzutowanie z operatorem as, związane instrukcją with — ona pozwala przeprowadzić wiele czynności na zrzutowanej referencji i jednocześnie uniknąć wykorzystania zmiennej pomocniczej. Jak kto woli.

1

Ja bym dodał dwie uwagi:

  1. Przy niezgodności typów rzutowanie twarde wywala AV często razem z programem, zaś miękkie wyjątek "Invalid class typecast" i wylatuje z procedury. Czasem to ogromna różnica :D
  2. Rzutowanie twarde jest szybsze i często widać to zwłaszcza przy obsłudze różnych OnDraw*.
0
dziobu napisał(a):
  1. Przy niezgodności typów rzutowanie twarde wywala AV często razem z programem, zaś miękkie wyjątek "Invalid class typecast" i wylatuje z procedury. Czasem to ogromna różnica :D

Jak się nie implementuje obsługi wyjątków, to nie dziwne, że proces zakańcza działanie. :D

1 użytkowników online, w tym zalogowanych: 0, gości: 1