Jun 11

Приведение типов

Мне всегда казалось, что TType(Var) и Var as TType работает одинаково или, в крайнем случае, очень похоже. Так было до не давнего времени.

Вместе со знакомым выясняли одну багу (так и не выяснили причину, кстати):

program Project1;

{$APPTYPE CONSOLE}

uses
  SysUtils;

type
  TA = class
    function GetResult: Integer; //virtual;
  end;

  TB = class(TA)
    function GetResult: Integer; //override;
  end;

{ TA }

function TA.GetResult: Integer;
begin
  Result := 10;
end;

{ TB }

function TB.GetResult: Integer;
begin
  Result := 20;
end;

var
  B: TB;

begin
  B := TB.Create;
  WriteLn(IntToStr(TA(B).GetResult));
  WriteLn(IntToStr((B as TA).GetResult));

  ReadLn;
end.

Как по-вашему, что будет выведено, в результате данного кода? Если вы считаете, что будет выведено два раза '10', то вы не угадали. Будет выведено '10' и '20'.

Т.е. (B as TA).GetResult вызовет метод GetResult класса TB, а не TA. Природу данного глюка мне познать, пока еще, не удалось. Выводы пока-то не однозначные: при приведении типа через AS, результат необходимо присваивать какой-то переменной, т.к. иначе он "теряется". Такие выводы появились в связи с тем, что следующий код работает как надо:

var
  B: TB;

  D: TA;

begin
  B := TB.Create;
  WriteLn(IntToStr(TA(B).GetResult));

  D := B as TA;
  WriteLn(IntToStr(D.GetResult));

  ReadLn;
end.

Так что на будущее - аккуратнее с типами.

P.S.: кстати, напоминаю, что конкурс продолжается, вот только пока что ни одной статьи я не увидел. Огорчает.


  1. Корвин
     Add karma Subtract karma  +1
    Цитировать.

    Цитата из книжки

    Оператор as введен в язык специально для приведения объектных типов.
    С его помощью можно рассматривать экземпляр объекта как принадлежащий к другому совместимому типу:
    with ASomeObject as TAnotherType do…
    От стандартного способа приведения типов с помощью конструкции
    TAnotherType (ASomeObject) использование оператора as отличается наличием
    проверки на совместимость типов во время выполнения (как в операторе
    is): попытка приведения к несовместимому типу приводит к возникнове-
    нию исключительной ситуации EinvaiidCast (см. гл. 4).
    После применения оператора as сам объект остается неизменным, но вызываются те его методы, которые соответствуют присваиваемому классу.

    1
  2. aktuba
     Add karma Subtract karma  +0
    Цитировать.

    Так вот в том то и проблема, что вызываются другие методы.

    WriteLn(IntToStr(TA(B).GetResult));
    В этом случае вызывается метод класса TA.

    WriteLn(IntToStr((B as TA).GetResult));
    А в этом метод класса TB, хотя тоже должен вызываться TA.

    2
  3. Корвин
     Add karma Subtract karma  +0
    Цитировать.

    “После применения оператора as сам объект остается неизменным, но вызываются те его методы, которые соответствуют присваиваемому классу.”
    Если напрячься то можно определить, что вызываются МЕТОДЫ САМОГО ОБЪЕКТА, а не того к которому от приведен, просто эти методы, грубо говоря, так же называются и имеют такие же параметры как и у объекта к которому приводится.
    Если же говорить о приведении типа TAnotherType (ASomeObject) то при этом уже при вызове метода – идет поиск метода в VMT от TAnotherType и вызова его оттуда.

    3
  4. December
     Add karma Subtract karma  +0
    Цитировать.

    Если отбросить нюанс с проверкой совместимости (в нашем случае типы совместимы), то остаётся только вот что:
    После применения оператора as сам объект остается неизменным, но вызываются те его методы, которые соответствуют присваиваемому классу.
    При классическом кастовании TA(B) сам объект тоже не меняется. И методы должны вызываться от ТА, иначе зачем вообще кастовать.

    Хочу также обратить внимание, что речь идёт о НЕвиртуальных методах. С виртуальными всё по-другому.

    4
  5. egalex
     Add karma Subtract karma  +0
    Цитировать.

    Действительно, очень интересно. Поскольку подстановка статических вызовово происходит в compile-time, то получается, что формальный тип выражения (X as TY) зависит от связи между формальным типом X и типом TY. Формальный тип – это грубо говоря как объявлена переменная. Таким образом, получается, что
    – если FormalType(X) является базовым для TY, то тип выражения – TY,
    – если FormalType(X) является производным от TY, то тип выражения – FormalType(X),
    (- если FormalType(X) и TY не связаны отношением наследования, то выдается ошибка компиляции).

    С точки зрения реализации as и () отличаются только динамической проверкой фактического типа и если фактический тип проверен на этапе компиляции (второй случай), то эта проверка в код не вставляется. Но зачем они сделали таким сложным выведение формального типа, не понятно.

    5
  6. Metal
     Add karma Subtract karma  +0
    Цитировать.

    имхо в правильно спроектированных иерархиях классов таких ситуаций, как наличие одноименных невиртуальных методов у предка и наследников, быть не должно.

    6
  7. aktuba
     Add karma Subtract karma  +0
    Цитировать.
    Комментарий автора

    Так с этим никто не спорит, но всякое бывает в жизни.

    7
  8. Мохов Михаил
     Add karma Subtract karma  +2
    Цитировать.

    Коллеги, заковыка тут есть, но чтобы разобоаться в ней, надо чутка изменить код, чтобы стало понятнее:

    {$APPTYPE CONSOLE}

    uses
    SysUtils;

    type
    TA = class
    procedure GetResult;
    end;

    TB = class(TA)
    procedure GetResult;
    end;

    { TA }

    procedure TA.GetResult;
    begin
    WriteLn(ClassName + ‘ => ‘ + ‘Method TA’);
    end;

    { TB }

    procedure TB.GetResult;
    begin
    WriteLn(ClassName + ‘ => ‘ + ‘Method TB’);
    end;

    var
    B: TB;

    begin
    B := TB.Create;
    try
    TA(B).GetResult;
    (B as TA).GetResult;
    ReadLn;
    finally
    B.Free;
    end;
    end.

    Теперь хорошо видно, что и в первом, и во втором случае экземпляр считает себя TB, но методы вызываются разные. Как вы правильно определили, дело в работе оператора as. И в этом нет ничего удивительного, потому что для своей работы операторы is и as, в отличии от явного приведения, используют RTTI информацию. В документации про это почти ничего нет – отсюда и недопонимание.

    8
  9. NDee
     Add karma Subtract karma  +0
    Цитировать.

    Вот-вот! До меня это дошло не с первого раза:)

    9
  10. arcda
     Add karma Subtract karma  +0
    Цитировать.

    +1 🙂

    10

Поделитесь вашими мыслями



Ссылки в комментариях будут свободны от nofollow.

Поддерживаемые теги: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong> 

Ссылки в комментариях будут свободны от nofollow.

Пироги николай на заказ с доставкой - пирожковая 1 доставка пирогов kylinarium.ru. Кастрюля купить elmikon.ru.
-->