Реализация списка с записями

Дополнительный вопрос к этому вопросу: (обратите внимание, что это не дубликат , я прошу альтернативы здесь).

Есть ли способ сделать следующую работу:

type
  List <T> = record
  private
    FList  : TList <T>;
    FGuard : IInterface,
    procedure CheckCreated;
  public
    procedure Add(const Value : T);
  end;

procedure List <T>.CheckCreated;
begin
if (FGuard = nil) then
  begin
  FList := TList <T>.Create;
  FGuard := TGuard.Create (FList);    // guard calls free on list in destructor
  end;
end;

procedure List <T>.Add (const Value : T);
begin
CheckCreated;
FList.Add (Value);
end;

В идеале я хочу использовать его так:

function ReturnHandles : List <THandle>;
begin
Result.Add (2);
Result.Add (3);
end;

Как объясняется в ответах на связанный вопрос, это не работает (что очень жаль). Он не будет создавать новый список для каждого вызова.

К сожалению, это тоже не работает:

function ReturnHandles : List <THandle>;
begin
Initialize (Result);
Result.Add (2);
Result.Add (3);
end;

Это приводит к утечке защитных интерфейсов и всех списков, потому что Initialize просто перезаписывает ссылку на интерфейс без уменьшения счетчика ссылок.

Есть ли способ заставить это работать? Или вы бы предложили сделать это интерфейсом вместо записи и просто жить с линией построения?

function ReturnHandles : List <THandle>;
begin
Result := List <T>.Create;
Result.Add (2);
Result.Add (3);   
end;

Спасибо за вашу помощь!


person jpfollenius    schedule 16.12.2011    source источник
comment
Роб объяснил в вашем последнем вопросе, что Initialize было ошибкой в ​​ответе габра. Так что нет, вы не должны использовать это.   -  person David Heffernan    schedule 16.12.2011
comment
Это то, что я написал... вопрос в том, есть ли другие способы сделать эту работу или просто невозможно создать типы значений, содержащие объекты   -  person jpfollenius    schedule 16.12.2011
comment
Вы идете против правил в Delphi здесь. Вы можете продвинуться вперед, делая вещи таким образом, но вы обнаружите, что ваш подход типа RECORD дорого обходится.   -  person Warren P    schedule 17.12.2011
comment
@WarrenP, можете ли вы уточнить, каковы, по вашему мнению, большие затраты?   -  person jpfollenius    schedule 19.12.2011
comment
Например, собираетесь ли вы разрешать глубокие копии этих списков на основе записей?   -  person Warren P    schedule 19.12.2011
comment
@WarrenP Да, List.Copy. Неявные копии ведут себя так же, как ссылки (внутренняя ссылка на список копируется), что весьма приятно.   -  person jpfollenius    schedule 20.12.2011


Ответы (1)


Это должно работать нормально, если я правильно вас понимаю:

function ReturnHandles : List <THandle>;
begin
  Finalize(Result);
  Result.Add (2);
  Result.Add (3);
end;

Вызов Finalize гарантирует, что для всех управляемых типов установлено значение nil, что, как я полагаю, является вашим намерением.

Этот вопрос очень тесно связан с вашим предыдущим вопросом, и я думаю, что вы могли бы использовать параметры out для упрощения кода. Результат функции неявно является параметром var, но если вы использовали явный параметр out, он инициализирует управляемые типы по вашему желанию.

procedure InitializeHandles(out Handles : List <THandle>);
begin
  Handles.Add (2);
  Handles.Add (3);
end;

Лично я, поскольку вы вводите интерфейс в микс, я думаю, что был бы склонен идти до конца и использовать исключительно интерфейсы. Или используйте стандартные классы и примите необходимость управления временем жизни try/finally.

person David Heffernan    schedule 16.12.2011
comment
Спасибо, я пропустил эту информацию! Хотя не очень интуитивно понятно. Я мог бы пойти на решение интерфейса. К сожалению, тогда я потеряю возможность использовать перегрузку оператора :( - person jpfollenius; 16.12.2011
comment
Должен сказать, что я никогда не слышал об этой функции Initialize до вашего вчерашнего вопроса! - person David Heffernan; 16.12.2011
comment
Спасибо за отзыв! Это первый раз, когда я нахожу язык действительно ограничивающим. Либо я должен заставить всех вызывающих абонентов не забывать вызывать Finalize при таком использовании, либо я должен отказаться от перегрузки операторов и универсальных методов в списке. Я думаю, что возможны некоторые улучшения, чтобы сделать язык более универсальным. - person jpfollenius; 16.12.2011
comment
Пробовали ли вы использовать параметр out вместо функции? out имеет семантику, отличную от параметра var (что и является результатом функции), и должен делать то, что вы хотите. - person David Heffernan; 16.12.2011
comment
Параметр Out работает, но сводит на нет часть преимущества всей идеи: возможность написать более удобочитаемое Handles := GetHandles вместо GetHandles(Handles). - person jpfollenius; 16.12.2011
comment
@Smasher Да, я думаю, что здесь нет идеального решения, которое достигло бы всех ваших целей. - person David Heffernan; 16.12.2011