从Delphi中的func/proc获取对象作为结果
在delphi中,从函数/过程返回简单对象的最佳实践是什么 例:两种代码: 将创建的对象作为引用传递,在过程中填充对象,然后销毁它从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
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;