Delphi: С каких это пор ссылки на интерфейсы больше не выпускаются в конце блока with?

Недавно я наткнулся на проблему, вызванную некоторым очень старым кодом, который я написал, который, очевидно, предполагал, что ссылки на интерфейсы, используемые в with заявление будет выпущено, как только with-блок остался - вроде как неявный try-finally-блок (аналогично C#) usingзаявление, если я правильно понял).

По-видимому (в Delphi 2009) это не так (больше?). Кто-нибудь знает, когда это произошло? Или мой код был просто неправильным с самого начала?

Чтобы уточнить, вот упрощенный пример:

type
  IMyIntf = interface;
  TSomeObject = class(TInterfacedObject, IMyIntf)
  protected
    constructor Create; override; // creates some sort of context
    destructor Destroy; override; // cleans up the context created in Create
  public
    class function GetMyIntf: IMyIntf; //a factory method, calling the constructor
  end;

procedure TestIt;
begin
  DoSomething;
  with (TSomeObject.GetMyIntf) do
    begin
      DoStuff;
      DoMoreStuff;
    end; // <- expected: TSomeObject gets destroyed because its ref.count is decreased to 0
  DoSomethingElse;
end; // <- this is where TSomeObject.Destroy actually gets called

Всякий раз, когда кто-то начал староеwith это злой аргумент, это всегда был единственный пример, который я имел в виду, который заставлял меня говорить "Да, но...". Кажется, я был неправ... Кто-нибудь может подтвердить?

2 ответа

Решение

with сохраненное слово в Pascal/Delphi используется только для легкого доступа к членам записей или объектов / классов (т. е. чтобы не упоминать имя записи / объекта / класса). Это очень отличается от C# with что касается сбора мусора. Он существует на языке Паскаль со дня records родились, чтобы упростить вызов кода для многих членов данных (тогда они назывались просто "полями").

Подвести итоги, with не имеет ничего общего со сборкой мусора, освобождением памяти или уничтожением экземпляров объектов. Объекты, которые построены на with заголовок мог просто быть инициализирован в отдельной строке кода раньше, это то же самое.

Это СО-поведение никогда не менялось. Чтобы достичь ожидаемого поведения, вы можете изменить свой код следующим образом:

    procedure TestIt;
    var
       myIntf: IMyIntf; 
    begin
      DoSomething;

      myIntf := TSomeObject.GetMyIntf
      DoStuff;
      DoMoreStuff;
      myIntf := nil; // <- here is where TSomeObject.Destroy called

      DoSomethingElse;
    end; 

или вы можете сделать это в процедуре:

procedure TestIt;

   procedure DoAllStuff;
   var
      myIntf: IMyIntf; 
   begin
      myIntf := TSomeObject.GetMyIntf
      DoStuff;
      DoMoreStuff;
   end;  // <- here is where TSomeObject.Destroy called

begin    
  DoSomething;
  DoAllStuff;
  DoSomethingElse;
end; 
Другие вопросы по тегам