从Delphi中的func/proc获取对象作为结果

从Delphi中的func/proc获取对象作为结果,delphi,object,creation,Delphi,Object,Creation,在delphi中,从函数/过程返回简单对象的最佳实践是什么 例:两种代码: 将创建的对象作为引用传递,在过程中填充对象,然后销毁它 procedure Proc(var Obj: TMyObject); begin // populate Obj end; O := TMyObject.Create; try Proc(O); // manipulate populated object finally O.Free; end; 或 从函数中获取创建的对象,操作后销毁 fun

在delphi中,从函数/过程返回简单对象的最佳实践是什么

例:两种代码:

将创建的对象作为引用传递,在过程中填充对象,然后销毁它

procedure Proc(var Obj: TMyObject);
begin
  // populate Obj
end;

O := TMyObject.Create;
try
  Proc(O);
  // manipulate populated object
finally
  O.Free;
end;
从函数中获取创建的对象,操作后销毁

function Func: TMyObj;
begin
  Result := TMyObj.Create;
end;

O := Func;
if O <> nil then
begin
  try
    // manipulate
  finally
    O.Free;
  end;
end;
函数Func:TMyObj;
开始
结果:=TMyObj.Create;
结束;
O:=Func;
如果O为零,则
开始
尝试
//操纵
最后
O.免费;
结束;
结束;

这取决于对象的生命周期以及负责人。 大多数情况下,对象应由同一实体创建和销毁

假设您的方法用解析文件的结果填充TStringList。 是让该函数创建TStringList,还是创建它并作为引用传递

我发现在连续的代码行中创建它、作为引用传递它,然后销毁它更具可读性


现在让我们考虑一下,每个客户添加了一个返回TCube的函数。在这种情况下,我会使用一个函数,因为我假设我的实体会有一个客户列表,或者其他什么,负责在不需要时销毁他们。

我经常使用这个结构

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;

这样,我既可以将其用作带有传入引用的过程,也可以将其用作创建实例本身的函数。

我的规则是拥有所有权和创建权。我总是让创造者成为拥有者,因此我有责任摧毁这个物体。对象的创建在调用代码中是显式的,它从来不是调用的副作用

所以我的函数通常的签名是

function Func(o:tMyO): TMyO;
begin
  // ....
  Result := o;
end;
这样我也可以做

   o := func(TMyO.create);


让调用者创建对象并将其作为参数传递是一种常见的Delphi习惯用法。请注意,几乎在所有情况下都不必声明它
var

procedure Proc (Obj : TMyObject)
begin
  Obj.SomeProperty := 'SomeValue';
  ...
end;
呼叫代码:

Obj := TMyObject.Create;
try
  Proc (Obj);
finally
  FreeAndNil (Obj);
end;    
这避免了关于谁必须释放对象的混淆。请注意,如果您有一系列方法调用,那么跟踪需要释放的对象很快就会变得非常复杂

还有一个缺点:创建和销毁分散在代码中,因此无法使用
try…finally
块,这只是避免资源泄漏的另一个有用习惯用法


如果你想让你的方法创建对象,我会在函数名中明确它,像
CreateAndInitializeList
对我来说很合适。

没有最佳实践。但是你应该做的主要事情,就是确保在任何给定的时间,即使发生异常,谁负责销毁对象始终是明确的

函数创建一个新实例并返回它没有错。这样的函数是一个函数。您可以像对待类的构造函数一样对待它,因此您应该确保它的行为类似于构造函数:要么返回有效对象,要么抛出异常。它从不返回空引用

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”参数。这对调用者造成了某些限制,调用者必须使用与函数所请求的类型完全相同的变量,即使是创建了某个后代类型


这样的函数不应释放对象。调用方不向其调用的函数授予所有权责任,特别是当它计划在函数返回后使用对象时。

如前所述,一般来说,创建对象的同一实体应该释放它,这意味着调用方应该创建对象引用,而不是在函数内部创建对象引用

但是,只有在调用者知道要返回的项的确切类型而不是超类型时,这才可能实现。例如:

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”或其他指示符是很有帮助的。

-1我发现您的做法非常混乱。我宁愿创建两个方法(一个在创建对象后调用另一个),并使方法名清楚地表示正在发生的事情。当
Obj
是您自己方法的参数时,如果要调用
SomeFunction(Obj)
,该怎么办?你不知道它是否为nil,所以你不知道你是否必须释放返回的对象…我不理解你的评论“如果你想在Obj是你自己方法的参数时调用SomeFunction(Obj)怎么办?你不知道它是否为nil,所以你不知道你是否必须释放返回的对象…”。“当Obj是您自己方法的参数时”是什么意思???如果你能用一个代码示例来详细说明,那么我很乐意向你解释……问题是,你应该以一种方式编写方法,每个方法都有一个目的,而不是在一个方法中执行更多操作,因此你的解决方案是混乱的,不简单的
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;