TStringList作为函数结果,最小代码序列

4
如何从函数返回一个字符串列表已经讨论过很多次了。我的代码序列版本1只是回顾如何实现它。
function GetStrings : TStringList;
begin
  Result := TStringList.Create;
  Result.Add('string A');
  Result.Add('string B');
end;

procedure TForm1.Button1Click(Sender: TObject);
var stemp : tStringList;
begin
  stemp := GetStrings;
  MyListBox.items.addstrings(stemp);
  stemp.Free;
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  MyListBox.items.addstrings(GetStrings);
end;

在版本2中,有没有任何选项可以“无需额外代码”修复内存泄漏问题?这种方法到底有多危险?如果我的字符串列表只有几个字符串项,这会导致任何故障吗?是否不会出现内存不足的问题?


8
任何内存泄漏,无论多小,最终都可能导致内存不足异常。原因不是可用内存不足,而是内存碎片化。也就是说,如果你需要分配一些较大的内存块,在某个时刻你将无法这样做,因为没有足够大的连续内存块来容纳这样的分配。无论泄漏的大小如何,您都不应该容忍任何内存泄漏。 - Dalija Prasnikar
4个回答

14
不要使用 TStringList 作为结果类型,而是使用 TArray<string>。当然,这使得 version1 已经过时了。
function GetStrings : TArray<string>;
begin
  var lst := TStringList.Create;
  try
    lst.Add('string A');
    lst.Add('string B');
    Result := lst.ToStringArray;
  finally
    lst.Free;
  end;
end;

是的,这是我最喜欢的方法。 - Andreas Rejbrand
为什么这样更好? - Matthias B
3
@MatthiasB 因为TArray由编译器管理,当它超出范围时会自动释放。在这个例子中,我不会费心使用TStringList。我会直接填充数组,例如:function GetStrings : TArray<string>; begin SetLength(Result, 2); Result[0] := 'string A'; Result[1] := 'string B'; end; - Remy Lebeau
1
@RemyLebeau 我认为这应该很明显,这是削减的代码,以展示问题。另外,您的代码确实冗长。您可以使用 Result := ['foo', 'bar'];Result := TArray<string>.Create('foo', 'bar') - David Heffernan

4
如果你要从一个函数中返回一个新实例化的对象,有一个常用的模式。它是这样的:
function GetStrings: TStringList;
begin
  Result := TStringList.Create;
  try
    Result.Add('string A');
    Result.Add('string B');
  except
    Result.Free;
    raise;
  end;
end;

这个函数的行为就像一个构造函数。它要么返回一个新实例化的对象并将所有权交给调用者,要么引发异常并在引发异常之前进行清理。

如果您使用此模式,则需要以与由构造函数实例化的对象相同的方式执行,使用众所周知的模式:

procedure TForm1.Button1Click(Sender: TObject);
var 
  temp: TStringList;
begin
  temp := GetStrings;
  try
    MyListBox.Items.AddStrings(temp);
  finally
    temp.Free;
  end;
end;

现在,关于您的直接问题:

是否有任何选项可以在版本2中“无需额外代码”修复内存泄漏,这种方法到底有多危险,如果我的字符串列表只有几个字符串项,是否会导致任何故障,内存不足的问题不会发生?

如果您返回一个新实例化的对象,则调用者无法避免对该对象的所有权责任。


4

正如其他人所提到的,您需要在函数和调用者中添加额外的代码,以便正确管理TStringList,以确保它始终被释放。

更好的选择是根本不返回TStringList,而是接受调用者想要的任何TStrings对象并填充它,例如:

procedure GetStrings(Strings: TStrings);
begin
  Strings.BeginUpdate;
  try
    Strings.Add('string A');
    Strings.Add('string B');
  finally
    Strings.EndUpdate;
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  GetStrings(MyListBox.Items);
end;

1
GetStrings 不是一个适合追加到现有集合的函数的正确名称。 - David Heffernan
3
@DavidHeffernan 这是一种观点和个人喜好的问题。通常情况下没有什么问题,从功能上讲编译器不会在意。 - Remy Lebeau

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接