是否可以在Delphi中使用Fluent调用样式的自引用记录?



目标是创建一个名为TURLString的类型,按如下方式调用:

var
  newURl : TURLString;
begin
  newURL.Append('http://').Append('www.thehost.com').Append('path/on/server').Append('?');
  ...lots of app logic...
  newURL.AppendParam('name', 'value').Append('#').AppendParam('name', 'value');
  ...more params added...
  result := httpClient.Get(newURL);
end;

使用这样定义的TURLString(注意它是一个记录):

//from actual code used
TURLString = record
private
    FString : string;
public
    function Append(APart : string) : TURLString;
    function AppendParam(AParam, AValue : string) : TURLString;
end;
function TURLString.Append(APart: string) : TURLString;
begin
  FString := FString + APart;
  result := self;
end;
function TURLString.AppendParam(AParam, AValue: string): TURLString;
begin
  if (not Empty) then
    FString := FString + URL_AMB;
  FString := FString + AParam + '=' + AValue;
  result := self;
end;

当逐步执行流体调用时,值被附加,但退出时,它们恢复为传递给第一个追加调用的第一个字符串,newURL等于'http://'在调试追加调用时,您看到'http://www.thehost.com/path/on/server?name=value#name=value'。

这个概念可以用于记录吗?

为了消除记录复制的性能和内存使用问题,您可以使用指针作为方法的结果类型:

type
  PURLString = ^TURLString;
  TURLString = record
  private
    FString : string;
  public
    function Append(const APart : string) : PURLString;
    function AppendParam(const AParam, AValue : string) : PURLString;
  end;
function TURLString.Append(const APart: string) : PURLString;
begin
  FString := FString + APart;
  result := @self;
end;
function TURLString.AppendParam(const AParam, AValue: string): PURLString;
begin
  if FString <> '' then
    FString := FString + URL_AMB;
  FString := FString + AParam + '=' + AValue;
  result := @self;
end;

所以你可以调用你原来的TURLString,就像你希望的那样:

newURL.Append('http://').Append('www.thehost.com').Append(...

对于方法的字符串参数,不要忘记const关键字。

注意,正确使用指针的方法应该是
newURL.Append('http://')^.Append('www.thehost.com')^.Append(...

,但实际上Delphi编译器足够聪明,可以隐式地添加^符号。

使用这个技巧,您不必创建或释放您的newURL实例。注意,只有引用计数的形参(字符串、变量、接口)才会在堆栈上初始化:整型或双精度型将是纯随机的。

这正是我在SynProject文档工具中用于创建一些RTF内容的方法(至少在第一个版本中,现在我使用类而不是记录,因为我想添加继承)。

记录样式为每个呼叫创建一个新的,匿名的(因此无法访问)记录,Result = self;"当前"记录复制到新记录。这可能不是你想要的。正如David所说,您必须将(匿名)最终结果分配给您声明的记录变量,这样您才能最终访问最终结果。

如果您使用引用类型(对象或接口),它将返回对自身的引用,并且不会生成新对象(也不会复制任何对象)。

如果你使用的是像记录这样的值类型,那么你需要将最终返回的结果赋值给一个变量:

newURL := newURL.Append('http://').Append('www.thehost.com');

如果使用类实例之类的引用类型,则可以使用问题中使用的语法。

引用类型方法将数据类型视为可变的,而对于值类型,您最好实现不可变的数据类型。

看一下这里的TPathBuilder实现。它的主要目的是构建文件路径,但你可以理解。源代码包含在文章中。

最新更新