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.
FStuffis really supposed to be two strings. Some times we'll want to use it as two strings sometimes we'll want to insert it into a database as one string. It's not my code, but I do agree with you, for the most part we just changed all our shortstrings to string to avoid deprication.