Правило с goto, что мы используем, состоит в том, что goto хорошо к для переходящего вперед к единственной точке очистки выхода в функции. В действительно комплексных функциях мы ослабляем то правило позволить другой переход вперед. В обоих случаях мы избегаем глубоко вложенный, если операторы, которые часто происходят при проверке кода ошибки, которая помогает удобочитаемости и maintance.
Лучших методов нет. Однако главное, что вы должны сделать, это убедиться, что всегда ясно, кто несет ответственность за уничтожение объекта в любой момент времени, даже когда возникает исключение.
Нет ничего плохого в том, что функция создает новый экземпляр и возвращает его . Такой функцией является завод . Вы можете относиться к нему как к конструктору класса, поэтому вы должны убедиться, что он ведет себя как конструктор: либо возвращает действительный объект, либо генерирует исключение. Он никогда не возвращает пустую ссылку.
function Func: TMyObj;
begin
Result := TMyObj.Create;
try
Result.X := Y;
except
Result.Free;
raise;
end;
end;
Это шаблон обработки исключений, который вы не часто видите, но он важен для этого стиля работы. Возврат объекта передает право собственности от функции вызывающей стороне, но только в том случае, если ей удается полностью выполнить. Если он должен уйти раньше из-за исключения, он освобождает объект, потому что вызывающий не может освободить его сам. (Функции, которые завершаются из-за исключения, не имеют возвращаемых значений.) Вызывающий будет использовать его следующим образом:
O := Func;
try
writeln(O.X);
finally
O.Free;
end;
Если есть исключение в Func
, то O
никогда не будет назначен, так что вызывающему абоненту нечего делать бесплатно.
Когда вызывающий объект создает объект, а вы передаете его другой функции для его инициализации, не превращайте параметр в параметр «var». Это накладывает определенные ограничения на вызывающего объекта, который должен использовать переменную точно того типа, который запрашивается функцией, даже если вместо этого был создан какой-то тип-потомок.
Такая функция не должна не освобождать объект. Вызывающий не передает ответственность за владение вызываемыми им функциями, особенно если он планирует использовать объект после возврата из функции.
Такая функция должна не освобождать объект. Вызывающий не передает ответственность за владение вызываемыми им функциями, особенно если он планирует использовать объект после возврата из функции.
Такая функция должна не освобождать объект. Вызывающий не передает ответственность за владение вызываемыми им функциями, особенно если он планирует использовать объект после возврата из функции.
Это зависит от времени жизни объекта и от того, кто за него отвечает. В большинстве случаев объекты должны создаваться и уничтожаться одной и той же сущностью.
Допустим, ваш метод заполняет TStringList результатами синтаксического анализа файла. Должны ли вы позволить этой функции создать TStringList или вы должны создать его и передать в качестве ссылки?
Я считаю более читаемым создать его, передать в качестве ссылки, а затем уничтожить все в последовательных строках кода.
] Теперь давайте предположим, что у вас есть функция, которая возвращает TCustomer для каждого добавленного клиента. В этом случае я бы использовал функцию, потому что я предполагаю, что у моей организации будет список или что-то в этом роде клиентов, ответственных за их уничтожение, когда они не нужны.
Это обычная идиома Delphi, позволяющая вызывающему объекту создать объект. и передайте его как параметр. Обратите внимание, что вам не нужно объявлять его var
почти во всех случаях.
procedure Proc (Obj : TMyObject)
begin
Obj.SomeProperty := 'SomeValue';
...
end;
Код вызова:
Obj := TMyObject.Create;
try
Proc (Obj);
finally
FreeAndNil (Obj);
end;
Это позволяет избежать путаницы в отношении того, кто должен освободить объект.
Мое правило - владеть и создавать все вместе. Создатель всегда является владельцем, и поэтому я несу ответственность за уничтожение объекта. Создание объекта явно указано в коде вызова, это никогда не является побочным эффектом вызова.
Таким образом, обычные подписи моих функций
function Func(o:tMyO): TMyO;
begin
// ....
Result := o;
end;
, таким образом, я могу сделать либо
o := func(TMyO.create);
, либо
o := TMyO.create;
// ...
func(o);
Как уже упоминалось, обычно тот же объект, который создал объект, должен освободить его, а это означает, что вызывающий должен создать ссылку на объект, а не делать это внутри функции.
Однако, это возможно, только если вызывающий знает точный тип возвращаемого элемента, а не супертип. Например:
var E: TEmployee;
E := CreateEmployee(EmployeeID); // Could return TEmployee or subclasses TManager or TRetiredEmployee
try
E.SendEmail(MessageText);
if (E is TRetiredEmployee) then
E.PrintLetter;
finally
E.Free;
end;
В подобных случаях я считаю полезным включить слово «Create» или другой индикатор в имя вызываемой мной фабричной функции.
Я часто использую конструкцию
FUNCTION SomeFunction(SL : TStrings = NIL) : TStrings;
BEGIN
IF Assigned(SL) THEN Result:=SL ELSE Result:=TStringList.Create;
// Use Result for the remainder of the function
END;
Таким образом, я могу использовать ее как ПРОЦЕДУРУ с переданной ссылкой, так и как ФУНКЦИЮ, которая создает сам экземпляр.