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

发布于 2024-12-02 20:17:50 字数 704 浏览 0 评论 0原文

作为从 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 作为参数。有什么方法可以对我们想要传递给 const 数组的内容进行隐式转换吗?

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

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

We got rid of shortstring as part of a conversion from Delphi 7. I wanted to make it as painless as possible so we figured we could change the ShortString to some record which acted in the same way. Here's how it's declared (there's more to it, but this is the basic structure, which outlines the problem):

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

This works well for setting strings to the record. But then there's functions like format which take as its parameter const array of const's. Is there any way to do an implict cast to what we'd want to pass into a const array?

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

Gives a syntax error while compiling because ssr is not a type of parameter that you can use on one of those arrays.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

橘和柠 2024-12-09 20:17:50

简短回答:否。
长答案:您要求的是编译器以某种方式知道您希望将本质上无类型的参数强制转换为您想要的类型。编译器在调用站点没有足够的信息来做出决定。如果添加“显式”运算符,然后将参数显式转换为字符串,那么它将起作用。

Short answer: No.
Long answer: What you're asking for is that the compiler to somehow know that you want a inherently untyped parameter to be coerced into the type you intend. The compiler simply doesn't have enough information at the call-site to make the determination. If you add an "Explicit" operator and then explicitly cast the parameter to a string, then it will work.

话少心凉 2024-12-09 20:17:50

您可以将以下内容添加到公共声明中:

function AsAnsiString : AnsiString;
function AsShortString : ShortString;

然后显式使用您想要使用的强制转换:

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

You could add the following to the public declaration :

function AsAnsiString : AnsiString;
function AsShortString : ShortString;

Then explicitly use the cast you want to use :

result := Format('hello %s',[ssr.AsAnsiString]);
许仙没带伞 2024-12-09 20:17:50

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

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

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;    

FWIW 这是实现:

{ 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 记录编写了 1 个文件。 (有一个中间步骤会自动将所有 ShortString 类型更改为我们自己的类型,并将单位 StringTypes 添加到使用中,但这是安全的。)数千个与 ShortString 相关的警告消失了。

I did something very similar in our migration from Delphi 2007 and also discovered that you can't use Format() to pass records to, and after reading the comments it makes perfect sense. An explicit cast (ideally to string) will tell the compiler what to do; the "explicit" methods are not required, however. As for suggestions to use "AsAnsiString": I personally don't like this idea because 1) extra function to write whereas an explicit cast can do the job 2) if readability is important, then so should be consistency, i.e. you do TShortStringRec.AsAnsiString, but do you also need to add an explicit method to set data, like SetAsAnsiString (or just do AsAnsiString as a property)? To me this defeats the point of implicit class operators. I recommend to stick to explicit casts, let the compiler determine which call is correct.

We use a lot of string[] types, so I auto-generated all my records. I thought it would be better to specify a default property to get AnsiChars out of ShortString types rather than have them get converted to UnicodeString then getting the char through [ ], for example:

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;    

FWIW here's the implementation:

{ 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;

This has been turned out to be overall a good trick because we didn't manually fiddle with hundreds of files, instead, just wrote 1 file with all our custom ShortString records with implicit class operators. (There was an intermediate step that automatically changed all ShortString types to our own and added the unit StringTypes to the uses, but it was safe.) Thousands of ShortString related warnings disappeared.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文