AnsiString

Top  Previous  Next

Pretranslated C# code > Delphi RTL > AnsiString

Delphi2C# converts the Delphi AnsiString type to System.AnsiString. This runtime class represents a byte-based Delphi long string and keeps it distinct from C# string, which contains UTF-16 characters.

 

The separate type allows translated code to preserve byte counts, code pages, embedded null bytes, PAnsiChar operations, array of const type information, and byte-exact memory operations. Converting AnsiString to C# string too early would change these semantics.

 

The generated code normally imports the required runtime declarations with:

 

   using System;

   using static System.SystemInterface;

   using static System.Sysutils.SysutilsInterface;

 

Runtime representation

 

 

System.AnsiString stores a byte sequence and a code page. Its private storage also contains a trailing zero byte for pointer interoperability. The trailing zero is not part of the value and is not included in Length.

 

The runtime class has the following properties:

 

Length is the number of data bytes, including embedded zero bytes.
The C# indexer is zero-based and returns byte.
AnsiChar is represented as byte in generated C#.
Values are immutable.
Equality, ordering, and hashing compare the stored bytes.
The code page is conversion metadata and is not part of byte equality.
Runtime helper functions treat null like an empty Delphi long string.

 

Generated variables are initialized with AnsiString.Empty:

 

Delphi:

 

var

  S: AnsiString;

 

C#:

 

AnsiString S = AnsiString.Empty;

 

Assignments and construction

 

A string literal assigned to a Delphi AnsiString remains a string literal in the generated code. The implicit string-to-AnsiString conversion encodes the literal with the configured default code page.

 

Delphi:

 

    S := 'Hello';

 

C#:

 

    S = "Hello";

 

When the source expression is already a byte sequence, Delphi2C# and the RTL use AnsiString.FromBytes so that no character conversion takes place:

 

    AnsiString value = AnsiString.FromBytes(

        new byte[] { 0x41, 0x80, 0x00, 0x42 },

        1252);

 

When the source is Unicode text and the Delphi operation requires an ANSI conversion, the RTL uses AnsiString.FromString with the selected code page:

 

    AnsiString value = AnsiString.FromString(text, 1252);

 

An AnsiChar expression is generated as byte. The runtime provides an implicit byte-to-AnsiString conversion for Delphi assignments and concatenations:

 

Delphi:

 

    C := 'A';

    S := C;

 

C#:

 

    C = (byte)'A';

    S = C;

 

Code pages

 

 

An unqualified Delphi AnsiString uses code page zero, also known as CP_ACP. Delphi2C# resolves code page zero through AnsiStringSettings.DefaultCodePage when a value is created. The default value is Windows-1252, matching a typical US Windows installation.

 

    AnsiStringSettings.DefaultCodePage = 1252;

 

Existing AnsiString values retain their resolved code page if the process-wide default is changed later.

 

AnsiStringSettings.ConversionMode controls invalid or lossy conversions. DelphiCompatible uses replacement fallback. Strict uses exception fallback and reports conversions that cannot be performed without loss.

 

    AnsiStringSettings.ConversionMode =

        AnsiStringConversionMode.DelphiCompatible;

 

The runtime method Reencode decodes a value with its current code page and encodes it with the requested target code page. WithCodePage(codePage, false) changes only the code page tag and leaves all bytes unchanged. This models a Delphi SetCodePage operation with conversion disabled.

 

RawByteString is also converted to System.AnsiString. A RawByteString value can carry the neutral code page 65535. Its bytes remain available for copying and comparison, but decoding requires a concrete code page. If a concrete AnsiString value is passed to a RawByteString parameter, its runtime code page is preserved.

 

Length and element access

 

 

Delphi AnsiString indexes are one-based. The System.AnsiString indexer follows the C# convention and is zero-based. Delphi2C# therefore subtracts one from a Delphi element index.

 

Delphi:

 

    C := S[1];

 

C#:

 

    C = S[1 - 1];

 

The indexed value has type byte because Delphi AnsiChar is mapped to byte.

 

System.AnsiString is immutable, so an indexed write cannot be emitted as an assignment to the indexer. Delphi2C# emits AnsiString.SetByte and passes the destination variable by reference.

 

Delphi:

 

    S[1] := 'A';

 

C#:

 

    AnsiString.SetByte(ref S, 1 - 1, (byte)'A');

 

The Delphi Length function is bound to the AnsiString overload in SystemInterface and returns the byte count:

 

Delphi:

 

    L := Length(S);

 

C#:

 

    L = Length(S);

 

High returns the Delphi high string index, which is equal to Length. Low returns one. Generated element access still subtracts one after either value has been calculated.

 

 

SetLength and value changes

 

Delphi2C# binds SetLength to the AnsiString overload and passes the destination by reference:

 

Delphi:

 

    SetLength(S, 10);

 

C#:

 

    SetLength(ref S, 10);

 

The runtime replaces S with a new immutable value. Shrinking preserves the requested prefix. Growing preserves the existing bytes and adds zero bytes. Delphi defines newly allocated string data as unspecified; zero initialization makes translated C# behavior deterministic and memory safe.

 

Other Delphi operations that modify a string also replace the destination through a ref parameter. This preserves the observable copy-on-write behavior of Delphi strings when multiple variables previously referred to the same value.

 

Copy, Pos, Insert, and Delete

 

Delphi2C# binds the standard string functions to AnsiString overloads instead of converting the operands to C# string.

 

The generator applies the same index transformation used for C# string. Copy, Insert, and Delete receive an already translated zero-based position:

 

Delphi:

 

    Part := Copy(S, 2, 3);

    Insert('XY', S, 3);

    Delete(S, 2, 3);

 

C#:

 

    part = Copy(S, 2 - 1, 3);

    Insert((AnsiString)"XY", ref S, 3 - 1);

    Delete(ref S, 2 - 1, 3);

 

The cast on an untyped literal makes C# select the AnsiString overload chosen from the Delphi source types.

 

Pos retains its Delphi result contract. It returns a one-based byte position, or zero when the substring is not found:

 

Delphi:

 

    Position := Pos('cat', S);

 

C#:

 

    position = Pos((AnsiString)"cat", S);

 

System.AnsiString also provides the C#-style instance method IndexOf. That method accepts and returns zero-based indexes and returns -1 when no match is found. Delphi Pos calls are normally kept as RTL calls so their one-based result is preserved.

 

 

Concatenation and comparison

 

Delphi AnsiString concatenation is emitted with the C# + operator. The runtime supports AnsiString operands and string literals:

 

Delphi:

 

    Result := A + ' ' + B;

 

C#:

 

    result = A + " " + B;

 

If two AnsiString operands have different code pages, the right operand is converted to the code page of the left operand before concatenation.

 

The comparison operators compare bytes. Two values with identical bytes but different code page tags compare as equal and have identical hash codes. Comparison with a C# string literal encodes the literal using the code page of the AnsiString operand.

 

Locale-aware, case-insensitive, and other RTL comparisons are bound to their typed AnsiString overloads. Delphi2C# does not replace such calls with the byte-order operators.

 

 

Unicode conversion boundaries

 

The conversion from C# string to AnsiString is implicit to support generated literal assignments. The conversion from AnsiString to C# string is explicit. This prevents an ANSI value from silently becoming Unicode before a byte-based operation.

 

When the receiving Delphi type is UnicodeString, string, or another Unicode type, Delphi2C# emits an explicit conversion boundary such as Decode():

 

    string text = value.Decode();

 

An explicit C# cast has the same text-conversion behavior:

 

    string text = (string)value;

 

ToString also decodes the value. Generated runtime code normally uses Decode where the ANSI-to-Unicode boundary needs to remain visible.

 

Conversion between AnsiString and byte[] copies bytes and performs no text encoding:

 

    byte[] bytes = value.ToByteArray();

    AnsiString copy = AnsiString.FromBytes(bytes, value.CodePage);

 

 

PAnsiChar conversion

 

Delphi2C# maps PAnsiChar to System.PAnsiChar and keeps it distinct from PChar. PAnsiChar addresses bytes, while PChar addresses UTF-16 characters.

 

A Delphi pointer cast is emitted as an explicit C# cast:

 

Delphi:

 

    P := PAnsiChar(S);

 

C#:

 

    P = (PAnsiChar)S;

 

The explicit cast returns a null pointer when S is empty, matching Delphi long-string behavior. The PAnsiChar constructor creates writable, null-terminated storage containing a copy of the AnsiString bytes.

 

Because System.AnsiString is immutable, writes through a copied pointer do not silently mutate the original value. Generated code that models a writable string buffer uses PAnsiChar.Synchronize or AnsiStringBuffer.Commit to copy the result back to the AnsiString variable. Commit(length) preserves embedded null bytes, while Commit() stops at the first null terminator.

 

 

Move operations

 

Delphi2C# emits typed Move overloads for AnsiString operands. The overloads copy raw bytes and never perform character encoding.

 

Delphi:

 

    Move(Source[5], Dest[3], 4);

 

C#:

 

    Move(Source, 5 - 1, ref Dest, 3 - 1, 4);

 

The source and destination offsets are zero-based byte offsets in the generated call. The count is always a byte count. The destination must already have enough storage, as it does after an appropriate assignment or SetLength call.

 

The RTL contains byte-preserving Move overloads between AnsiString, byte[], PAnsiChar, and UTF-16 string storage. A Move between ANSI and UTF-16 storage copies the memory representation exactly and does not decode or encode text. UTF-16 storage is handled as little-endian bytes to reproduce Delphi behavior independently of the host platform.

 

 

TVarRec and array of const

 

 

Delphi2C# preserves AnsiString as a separate array of const type. A statically typed AnsiString expression creates a TVarRec with VType vtAnsiString:

 

Delphi:

 

    Format('%s', [S]);

 

C#:

 

    Format("%s", new TVarRec[] { S });

 

The implicit TVarRec conversion delegates to TVarRec.FromAnsiString. Runtime code reads the byte-preserving value with ToAnsiStringValue.

 

The following TVarRec types remain distinct:

 

AnsiString is stored as vtAnsiString.
UnicodeString is stored as vtUnicodeString.
ShortString is stored as vtString.
PAnsiChar is stored as vtPChar.
PChar is stored as vtPWideChar.

 

ToStringValue decodes string-like TVarRec values to Unicode. Generated code uses ToAnsiStringValue when the original Delphi operation requires the bytes and code page to remain intact.

 

 

Overload selection

 

 

Delphi2C# resolves an overloaded Delphi function from the static Delphi types before inserting any C# conversions. This rule prevents a string literal or a premature Decode call from selecting a Unicode overload.

 

When a Delphi overload expects AnsiString and a literal alone would make C# prefer a string overload, the generated code adds an explicit AnsiString cast:

 

    result = UpperCase((AnsiString)"abc");

 

RawByteString and AnsiString arguments remain byte-backed while overload resolution takes place. They are decoded only if the selected Delphi declaration has a Unicode result or parameter boundary.

 

 

Translation summary

 

 

Delphi2C# applies the following AnsiString translation rules:

 

Delphi AnsiString and RawByteString become System.AnsiString.
Delphi AnsiChar becomes byte.
Delphi PAnsiChar becomes System.PAnsiChar.
String literals are encoded by the target AnsiString code page.
Length counts bytes.
Element indexes are translated from one-based Delphi indexes to zero-based C# indexes.
Indexed writes become AnsiString.SetByte calls with a ref destination.
Copy, Insert, Delete, SetLength, Pos, and related RTL calls bind to typed AnsiString overloads.
Pos results remain one-based.
Move copies bytes without implicit encoding.
ANSI-to-Unicode conversion is explicit.
array of const preserves vtAnsiString.
Pointer conversions preserve the PAnsiChar pointer family.

 

These transformations allow generated C# code to retain Delphi AnsiString

semantics without treating the value as a Unicode C# string.

 



This page belongs to the Delphi2C# Documentation

Delphi2C# home  Content