Note : The same rules for the class and record helpers applies, so you can define multiple helpers with a single type. However, only zero or one helper applies in any specific location in source code and only the record helper defined in the nearest scope will apply. The record helper scope is determined in the normal Delphi fashion (for example, right to left in the unit’s uses clause).

As you can see most of the strings related methods now are part of the string type, so now you can write code like this.

{$APPTYPE CONSOLE}
uses
System.SysUtils;
var
s,s1 :string;
begin
try
// Length property
s:='Hello Delphi XE3';
Writeln(Format('the string Length is %d',[s.Length]));
Writeln('The length of this literal string is '.Length);
//function Contains
if s.Contains('Delphi') then
Writeln(Format('the string "%s" contains the string "%s"',[s,'Delphi']));
//function EndsWith
if s.EndsWith('XE3') then
Writeln(Format('the string "%s" ends with the string "%s"',[s,'XE3']));
//function ToLower
Writeln(Format('using ToLower %s',[s.ToLower]));
//function ToUpper
Writeln(Format('using ToUpper %s',[s.ToUpper]));
//function IndexOf
Writeln(Format('The index of H is %d',[s.IndexOf('H')])); //the value is based in a zero index
//function LastDelimiter
Writeln(Format('The first occurence of any of these chars "abcdef" is %d',[s.IndexOfAny(['a','b','c','d','e','f'])])); //the value is based in a zero index
//function LastDelimiter
Writeln(Format('The last occurence of any of these chars "abcdef" is %d',[s.LastDelimiter('abcdef')])); //the value is based in a zero index
//function Remove
Writeln(Format('The string with only the first 5 chars is %s',[s.Remove(5)])); //the value is based in a zero index
//function Replace
Writeln(Format('Replacing the white spaces for "-", the string becomes %s',[s.Replace(' ','-')])); //the value is based in a zero index
//function split
Writeln;
Writeln('Testing the split function');
for s1 in s.Split([' ']) do
Writeln(s1);
Writeln;
//function Substring
Writeln(Format('The sub string starting in the index 6 is "%s"',[s.Substring(6)])); //the value is based in a zero index
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
readln;
end.

Note : All the methods and properties of the System.SysUtils.TStringHelper are zero based index, so be careful when you uses functions like CopyTo, IndexOf, IndexOfAny, Insert, Join, LastDelimiter, LastIndexOf, LastIndexOfAny, Remove, Substring, ToCharArray and the Chars property.

Finally exist a few set of record helpers included in the RTL code which you can use

Helpers were created for a very specific and limited purpose but have been co-opted and coerced them into a more general purpose use for which they were not only not intended but were clearly and specifically documented as not being intended for.

I don’t know of course, but I doubt that the internals of the implementation will easily allow for the extension of the specific into the general. Presumably the “restriction” arising from the implementation targeting the specific use case for which they were designed made that implementation itself easier – you wouldn’t deliberately do more work in order to achieve less. At least, not if you were sensible.

But given that everyone was warned not to use helpers as a general purpose solution then no-one really has anyone to blame but themselves if they run into problems with aspects of their implementation arising from their original design intent.

Jolyon I’m very aware of your position against the class and records helpers and I respect your points of view, but let me tell you which on my personal experience never I got any problem using the class helpers In fact this language feature has allowed me to solve many problems, that without the existence of these helpers would be very difficult to solve. Finally I think which the inclusion of record helpers for simple types add a lot advantages to write more cleaner code.

To me the smell is more in the declaration syntax (“record helper” for non-records), and the one helper limitation (which those other languages, or other Pascal favors for that matter) don’t have.
The “.” chaining form is more readable in many cases.