Delphi中的隐式类型转换:将重载记录作为const数组参数

5

我们在从Delphi 7转换时摒弃了ShortString。我希望让这个过程尽可能轻松,所以我们想改变ShortString为一些以同样方式运作的记录。以下是它的声明(还有更多内容,但这是基本结构,突出了问题):

TShortStringRec = record 
private
  FStuff: array [0..49] of Char;
public
  class operator Implicit(AStuff: TShortStringRec): String;
  class operator Implicit(S1: String): TShortStringRec;
end;

这对于向记录设置字符串的工作很好。但是像format这样的函数将const array of const作为其参数。有没有什么方法可以将隐式转换为我们想要传递到常量数组中的内容?

function FunkyFunc : string;
var
  ssr : TShortStringRec;
begin
  ssr := 'Wall'; 
  result := format('Hello %s', [ssr]);  //<---error here
end;

在编译时会出现语法错误,因为“ssr”不是您可以在其中一个数组上使用的参数类型。

1
@David,我省略了使记录有价值的其余部分。此外,我们不知道在尝试访问先前预期在字符串中具有的索引的部分。我们可以轻松地将其转换为字符串。如果您知道答案是否定的,即不可能,则请以这种方式回答。 - Peter Turner
我觉得很难想象你不能用字符串更好地解决这个问题。 - David Heffernan
这不是 variant 的作用吗? - Johan
@Johan,我也这么想,但是variant作为转换的结果类型不起作用。 - Peter Turner
1
实际上这是同一个问题:编译器不知道您想通过字符串将rec转换为变量。您需要告诉它。 - Rudy Velthuis
显示剩余2条评论
3个回答

7

简短回答: 不行。
详细回答: 你所要求的是编译器以某种方式知道你想将一个固有无类型参数强制转换为你想要的类型。然而,在调用时,编译器没有足够的信息来进行判断。如果你添加一个"Explicit"运算符并显式地将参数转换为字符串,则可以解决这个问题。


4
信息已存在,但其用途并不是语言定义的一部分。编译器知道记录类型不是可以传递给常量开放数组的已知类型。编译器可以查看记录类型,以查看是否存在到其中一个允许的内置类型之一的隐式转换,并且如果只有一个匹配,则执行该转换。 - dthorpe
5
@Peter - 因为让编译器允许您将记录传递给开放数组参数仅解决了一半的问题。另一半问题是接收函数(format)应该如何处理您的记录结构?Format()不知道该怎么做,所以将其作为记录传递进去没有任何意义。在进入开放数组之前,应将其转换为标准类型,以便只需处理有限数量的选项的函数可以采用开放数组的常量参数。 - dthorpe
1
@dthorpe - 然后在第二个版本中,您无意中添加了另一个隐式转换... 不知道它正在以这种方式使用。那么怎么办? - Allen Bauer
1
Allen,是否需要使用显式操作符?我认为将其转换为字符串应该已经按预期工作了,通过隐式调用Implicit(ShortStringRec): string - Rudy Velthuis
1
@Rudy,没错。如果你有一些需要解决的歧义,那么就需要显式声明。 - Allen Bauer
显示剩余5条评论

2
您可以将以下内容添加到公共声明中:
function AsAnsiString : AnsiString;
function AsShortString : ShortString;

然后明确使用您想要使用的转换:

result := Format('hello %s',[ssr.AsAnsiString]);

使用显式转换不是更容易吗? - David Heffernan
1
是的,但我认为这个转换提供了更多意图信息。这会产生更易读的代码。同时作为使用显式转换的替代方案也添加在这里。 - LU RD
这不是一个类型转换,而是一个方法。它如何提供比“AnsiString(ssr)”更多的信息? - David Heffernan

1

在我们从Delphi 2007迁移时,我做了类似的事情,并发现您不能使用Format()将记录传递给它。阅读评论后,这是有道理的。显式转换(最好是字符串)将告诉编译器该怎么做;但是,“显式”方法并不是必需的。至于使用“AsAnsiString”的建议:我个人不喜欢这个想法,因为1)需要额外的函数来编写,而显式转换可以完成工作2)如果可读性很重要,则一致性也应该很重要,即您执行TShortStringRec.AsAnsiString,但您是否还需要添加一个显式方法来设置数据,例如SetAsAnsiString(或者只需将AsAnsiString作为属性执行)?对我来说,这违背了隐式类运算符的初衷。我建议坚持使用显式转换,让编译器确定哪个调用是正确的。

我们使用了大量的string[]类型,因此我自动生成了所有记录。我认为最好指定一个默认属性,以从ShortString类型中获取AnsiChars,而不是将它们转换为UnicodeString,然后通过[ ]获取char,例如:

type
 _ShortString3 = string[3]:
  ShortString3 = record
  private
    FData: _ShortString3;
    function GetAnsiChar(Index: Integer): AnsiChar;
    procedure PutAnsiChar(Index: Integer; const Value: AnsiChar);
  public
    class operator Implicit(const A: string): ShortString3;
    class operator Implicit(const A: ShortString3): string;
    class operator Equal(const A: ShortString3; B: AnsiChar): Boolean;
    class operator NotEqual(const A: ShortString3; B: AnsiChar): Boolean;
    class operator Equal(const A: ShortString3; B: ShortString3): Boolean;
    class operator NotEqual(const A: ShortString3; B: ShortString3): Boolean;
    class operator Add(const A: ShortString3; B: ShortString3): string;
    class operator Add(const A: ShortString3; B: AnsiChar): string;
    class operator Add(const A: ShortString3; B: string): string;
    property AnsiChars[Index: Integer]: AnsiChar read GetAnsiChar write PutAnsiChar; default;
end;    

顺便提一下,这就是实现方式:

{ ShortString3 }

function ShortString3.GetAnsiChar(Index: Integer): AnsiChar;
begin
  Result := FData[Index];
end;

procedure ShortString3.PutAnsiChar(Index: Integer; const Value: AnsiChar);
begin
  FData[Index] := Value;
end;

class operator ShortString3.Implicit(const A: string): ShortString3;
begin
  Result.FData := _ShortString3(A);
end;

class operator ShortString3.Implicit(const A: ShortString3): string;
begin
  Result := string(A.FData);
end;

class operator ShortString3.Equal(const A: ShortString3; B: AnsiChar): Boolean;
begin
  Result := A.FData = B;
end;

class operator ShortString3.NotEqual(const A: ShortString3; B: AnsiChar): Boolean;
begin
  Result := A.FData <> B;
end;

class operator ShortString3.Equal(const A: ShortString3; B: ShortString3): Boolean;
begin
  Result := A.FData = B.FData;
end;

class operator ShortString3.NotEqual(const A: ShortString3; B: ShortString3): Boolean;
begin
  Result := A.FData <> B.FData;
end;

class operator ShortString3.Add(const A: ShortString3; B: ShortString3): string;
begin
  Result := string(A.FData + B.FData);
end;

class operator ShortString3.Add(const A: ShortString3; B: AnsiChar): string;
begin
  Result := string(A.FData + B);
end;

class operator ShortString3.Add(const A: ShortString3; B: string): string;
begin
  Result := string(A.FData) + B;
end;

这个操作被证明是一个非常好的技巧,因为我们没有手动处理数百个文件,而是只需编写一个包含所有自定义ShortString记录和隐式类操作符的文件。(中间步骤是将所有ShortString类型自动更改为我们自己的类型,并将StringTypes单元添加到uses中,但这是安全的。)成千上万与ShortString相关的警告消失了。

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