Поделиться через


Конструктор System.String

В этой статье приводятся дополнительные замечания к справочной документации по этому API.

Синтаксис перегруженного конструктора

Строковые конструкторы делятся на две категории: без параметров указателя, а также те, у которых есть параметры указателя. Конструкторы, использующие указатели, не совместимы с CLS. Кроме того, Visual Basic не поддерживает использование указателей, а для C# требуется код, использующий указатели для выполнения в небезопасном контексте. Дополнительные сведения см. в разделе unsafe.

Дополнительные рекомендации по выбору перегрузки см. в разделе "Какой метод вызывается?".

String(Char[] value)
Инициализирует новый экземпляр значением, указанным массивом символов Юникода. Этот конструктор копирует символы Юникода (пример 2. Использование массива символов).

String(Char[] value, Int32 startIndex, Int32 length)
Инициализирует новый экземпляр в значение, указанное массивом символов Юникода, начальной позицией символов в этом массиве и длиной (пример 3. Использование части массива символов и повторение одного символа).

String(Char c, Int32 count)
Инициализирует новый экземпляр к значению, указанному символом Юникода, повторяющим указанное число раз (пример 3. Использование части массива символов и повторение одного символа).

String(char* value)
(Не соответствует CLS) Инициализирует новый экземпляр значением, указанным указателем на массив символов Юникода, завершающегося символом NULL (U+0000 или \0). (Пример 4. Использование указателя на массив символов).

Разрешение: SecurityCriticalAttributeтребуется полное доверие для немедленного вызывающего абонента. Этот элемент не может использоваться частично доверенным или прозрачным кодом.

String(char* value, Int32 startIndex, Int32 length)
(Не соответствует CLS) Инициализирует новый экземпляр значением, указанным указателем на массив символов Юникода, начальную позицию в этом массиве и длину. Конструктор копирует символы Юникода, начиная с value индекса и заканчивая индексом startIndexlength + - 1 (пример 5. Создание экземпляра строки из указателя и диапазона массива).startIndex

Разрешение: SecurityCriticalAttributeтребуется полное доверие для немедленного вызывающего абонента. Этот элемент не может использоваться частично доверенным или прозрачным кодом.

String(SByte* value)
(Не соответствует CLS) Инициализирует новый экземпляр значением, указанным указателем на массив из 8-разрядных целых чисел со знаком. Предполагается, что массив представляет строку, закодированную с помощью текущей системной кодовой страницы (т. е. кодировки, указанной в параметре Encoding.Default). Конструктор обрабатывает символы начиная с value расположения, указанного указателем, до достижения значения NULL (0x00) (пример 6. Создание экземпляра строки из указателя на подписанный массив байтов).

Разрешение: SecurityCriticalAttributeтребуется полное доверие для немедленного вызывающего абонента. Этот элемент не может использоваться частично доверенным или прозрачным кодом.

String(SByte* value, Int32 startIndex, Int32 length)
(Не соответствует CLS) Инициализирует новый экземпляр значением, указанным указателем на массив 8-разрядных целых чисел со знаком, начальную позицию в этом массиве и длину. Предполагается, что массив представляет строку, закодированную с помощью текущей системной кодовой страницы (т. е. кодировки, указанной в параметре Encoding.Default). Конструктор обрабатывает символы из значения, начиная с startIndexstartIndexlength + 1 (пример 6. Создание экземпляра строки из указателя на подписанный массив байтов).

Разрешение: SecurityCriticalAttributeтребуется полное доверие для немедленного вызывающего абонента. Этот элемент не может использоваться частично доверенным или прозрачным кодом.

String(SByte* value, Int32 startIndex, Int32 length, Encoding enc)
(Не соответствует CLS) Инициализирует новый экземпляр в значение, указанное указателем на массив 8-разрядных целых чисел со знаком, начальную позицию в этом массиве, длину и Encoding объект.

Разрешение: SecurityCriticalAttributeтребуется полное доверие для немедленного вызывающего абонента. Этот элемент не может использоваться частично доверенным или прозрачным кодом.

Параметры

Ниже приведен полный список параметров, используемых String конструкторами, которые не включают параметр указателя. Параметры, используемые каждой перегрузкой, см. в приведенном выше синтаксисе перегрузки.

Параметр Тип Описание
value Char[] Массив знаков Юникода.
c Char Знак Юникода.
startIndex Int32 Начальная позиция первого value символа в новой строке.

Значение по умолчанию: 0
length Int32 Число символов, включаемых в value новую строку.

Значение по умолчанию: Array.Length
count Int32 Число повторов символа c в новой строке. Если count равно нулю, значение нового объекта равно String.Emptyнулю.

Ниже приведен полный список параметров, используемых String конструкторами, включающими параметр указателя. Параметры, используемые каждой перегрузкой, см. в приведенном выше синтаксисе перегрузки.

Параметр Тип Описание
value Char*

–или–

SByte*
Указатель на массив символов Юникода со значением NULL или массив целых чисел со знаком 8 бит. Если value массив или null пустой, значение новой строки равно String.Empty.
startIndex Int32 Индекс элемента массива, который определяет первый символ в новой строке.

Значение по умолчанию: 0
length Int32 Количество элементов массива, используемых для создания новой строки. Если длина равна нулю, конструктор создает строку, значение которой равно String.Empty.

Значение по умолчанию: Array.Length
enc Encoding Объект, указывающий, как value закодирован массив.

Значение по умолчанию: Encoding.Defaultили текущая кодовая страница ANSI системы

Исключения

Ниже приведен список исключений, создаваемых конструкторами, которые не включают параметры указателя.

Исключение Condition Вызывается
ArgumentNullException value имеет значение null. String(Char[], Int32, Int32)
ArgumentOutOfRangeException startIndexcount илиlength меньше нуля.

–или–

Сумма startIndex и length больше, чем число элементов в value.

–или–

count меньше нуля.
String(Char, Int32)

String(Char[], Int32, Int32)

Ниже приведен список исключений, создаваемых конструкторами, включающими параметры указателя.

Исключение Condition Вызывается
ArgumentException value указывает массив, содержащий недопустимый символ Юникода.

–или–

value или value + startIndex задает адрес, который меньше 64K.

–или–

Новый String экземпляр не удалось инициализировать из массива value байтов, так как value не использует кодировку кодовой страницы по умолчанию.
Все конструкторы с указателями.
ArgumentNullException Параметр value имеет значение null. String(SByte*)

String(SByte*, Int32, Int32)

String(SByte*, Int32, Int32, Encoding)
ArgumentOutOfRangeException Текущий процесс не имеет доступа на чтение ко всем рассматриваемым символам.

–или–

startIndex или length меньше нуля, value + startIndex приводит к переполнению указателя, или текущий процесс не имеет доступа на чтение ко всем адресуемым символам.

–или–

Длина новой строки слишком велика, чтобы выделить.
Все конструкторы с указателями.
AccessViolationException value, или value + + startIndexlength - 1, указывает недопустимый адрес. String(SByte*)

String(SByte*, Int32, Int32)

String(SByte*, Int32, Int32, Encoding)

Какой метод вызывается?

По Вызов или использование
Создайте строку. Назначение из строкового литерала или существующей строки (пример 1. Использование назначения строк)
Создайте строку из всего массива символов. String(Char[]) (Пример 2. Использование массива символов)
Создайте строку из части массива символов. String(Char[], Int32, Int32) (Пример 3. Использование части массива символов и повторение одного символа)
Создайте строку, которая повторяет один и тот же символ несколько раз. String(Char, Int32) (Пример 3. Использование части массива символов и повторение одного символа)
Создайте строку из указателя в юникод или широкий массив символов. String(Char*)
Создайте строку из части массива символов Юникода или широкого символа с помощью указателя. String(Char*, Int32, Int32)
Создайте строку из массива C++ char . String(SByte*), String(SByte*, Int32, Int32)

–или–

String(SByte*, Int32, Int32, Encoding)
Создайте строку из символов ASCII. ASCIIEncoding.GetString

Создание строк

Наиболее часто используемый метод для создания строк программным способом — простое назначение, как показано в примере 1. Класс String также включает четыре типа перегрузки конструктора, которые позволяют создавать строки из следующих значений:

  • Из массива символов (массив символов в кодировке UTF-16). Вы можете создать объект String из символов во всем массиве или его части. Конструктор String(Char[]) копирует все символы в массиве в новую строку. Конструктор String(Char[], Int32, Int32) копирует символы из индекса в индекс + startIndexstartIndexlength — 1 в новую строку. Если length значение равно нулю, значение новой строки равно String.Empty.

    Если код неоднократно создает экземпляры строк с одинаковым значением, вы можете повысить производительность приложения с помощью альтернативного средства создания строк. Дополнительные сведения см. в разделе "Обработка повторяющихся строк".

  • Из одного символа, дублирующего ноль, один или несколько раз с помощью конструктора String(Char, Int32) . Если count значение равно нулю, значение новой строки равно String.Empty.

  • От указателя до массива символов, завершающегося значением NULL, с помощью String(Char*) конструктора или String(Char*, Int32, Int32) конструктора. Для инициализации строки можно использовать весь массив или указанный диапазон. Конструктор копирует последовательность символов Юникода, начиная с указанного указателя или из указанного указателя плюс startIndex и продолжая до конца массива или символов length . Если value имеет значение NULL или length равно нулю, конструктор создает строку, значение которой равно String.Empty. Если операция копирования продолжается до конца массива и массив не завершается null, поведение конструктора зависит от системы. Такое условие может привести к нарушению доступа.

    Если массив содержит все внедренные символы NULL (U+0000 или \0) и String(Char*, Int32, Int32) вызывает перегрузку, экземпляр строки содержит length символы, включая все внедренные значения NULL. В следующем примере показано, что происходит при передаче указателя на массив из 10 элементов, включающих два пустых символа String(Char*, Int32, Int32) . Так как адрес является началом массива, а все элементы в массиве добавляются в строку, конструктор создает экземпляр строки с десятью символами, включая два внедренных null. С другой стороны, если тот же массив передается String(Char*) конструктору, результатом является четырехзначная строка, которая не включает первый пустой символ.

    using System;
    
    public class Example2
    {
       public unsafe static void Main()
       {
          char[] chars = { 'a', 'b', 'c', 'd', '\0', 'A', 'B', 'C', 'D', '\0' };
          string s = null;
          
          fixed(char* chPtr = chars) {
             s = new string(chPtr, 0, chars.Length);            
          } 
    
          foreach (var ch in s)
             Console.Write($"{(ushort)ch:X4} ");
          Console.WriteLine();
          
          fixed(char* chPtr = chars) {
             s = new string(chPtr);         
          }
          
          foreach (var ch in s)
             Console.Write($"{(ushort)ch:X4} ");
          Console.WriteLine();    
       }
    }
    // The example displays the following output:
    //       0061 0062 0063 0064 0000 0041 0042 0043 0044 0000
    //       0061 0062 0063 0064
    
    #nowarn "9"
    open System
    
    let chars = [| 'a'; 'b'; 'c'; 'd'; '\000'; 'A'; 'B'; 'C'; 'D'; '\000' |]
    let s =
        use chPtr = fixed chars
        String(chPtr, 0, chars.Length)
    
    for ch in s do
        printf $"{uint16 ch:X4} "
    printfn ""
    
    let s2 = 
        use chPtr = fixed chars
        String chPtr    
    
    for ch in s2 do
        printf $"{uint16 ch:X4} "
    printfn ""  
    // The example displays the following output:
    //       0061 0062 0063 0064 0000 0041 0042 0043 0044 0000
    //       0061 0062 0063 0064
    

    Массив должен содержать символы Юникода. В C++это означает, что массив символов должен быть определен как управляемый Charтип [] или неуправляемыйwchar_t[] тип.

    String(Char*) Если перегрузка вызывается и массив не завершается значением NULL, или если String(Char*, Int32, Int32) перегрузка вызывается startIndex + lengthи -1 включает диапазон, который находится за пределами памяти, выделенной для последовательности символов, поведение конструктора зависит от системы, а нарушение доступа может произойти.

  • Из указателя на подписанный массив байтов. Для инициализации строки можно использовать весь массив или указанный диапазон. Последовательность байтов может быть интерпретирована с помощью кодировки кодовой страницы по умолчанию, или кодировку можно указать в вызове конструктора. Если конструктор пытается создать экземпляр строки из всего массива, не завершающегося значением NULL, или если диапазон массива от valuestartIndexstartIndexlength + value + + -1 находится вне памяти, выделенной для массива, поведение этого конструктора зависит от системы, а нарушение доступа может произойти.

    Три конструктора, включающие подписанный массив байтов в качестве параметра, предназначены в основном для преобразования массива C++ char в строку, как показано в этом примере:

    using namespace System;
    
    void main()
    {
          char chars[] = { 'a', 'b', 'c', 'd', '\x00' };
          
          char* charPtr = chars;
          String^ value = gcnew String(charPtr);
    
          Console::WriteLine(value);
    }
    // The example displays the following output:
    //      abcd
    

    Если массив содержит любые символы NULL ('\0') или байты, значение которых равно 0, а String(SByte*, Int32, Int32) перегрузка вызывается, экземпляр строки содержит length символы, включая все внедренные значения NULL. В следующем примере показано, что происходит при передаче указателя на массив из 10 элементов, включающих два пустых символа String(SByte*, Int32, Int32) . Так как адрес является началом массива, а все элементы в массиве добавляются в строку, конструктор создает экземпляр строки с десятью символами, включая два внедренных null. С другой стороны, если тот же массив передается String(SByte*) конструктору, результатом является четырехзначная строка, которая не включает первый пустой символ.

    using System;
    
    public class Example5
    {
       public unsafe static void Main()
       {
          sbyte[] bytes = { 0x61, 0x62, 0x063, 0x064, 0x00, 0x41, 0x42, 0x43, 0x44, 0x00 };
          
          string s = null;
          fixed (sbyte* bytePtr = bytes) {
             s = new string(bytePtr, 0, bytes.Length);
          }
          
          foreach (var ch in s)
             Console.Write($"{(ushort)ch:X4} ");
          
          Console.WriteLine();    
    
          fixed(sbyte* bytePtr = bytes) {
             s = new string(bytePtr);         
          }
          
          foreach (var ch in s)
             Console.Write($"{(ushort)ch:X4} ");
          Console.WriteLine();    
       }
    }
    // The example displays the following output:
    //       0061 0062 0063 0064 0000 0041 0042 0043 0044 0000
    //       0061 0062 0063 0064
    
    #nowarn "9"
    open System
    
    let bytes = 
        [| 0x61y; 0x62y; 0x063y; 0x064y; 0x00y; 0x41y; 0x42y; 0x43y; 0x44y; 0x00y |]
    
    let s =
        use bytePtr = fixed bytes
        String(bytePtr, 0, bytes.Length)
    
    for ch in s do
        printf $"{uint16 ch:X4} "
    printfn ""
    
    let s2 =
        use bytePtr = fixed bytes
        String bytePtr         
    
    for ch in s do
        printf $"{uint16 ch:X4} "
    printfn ""
    // The example displays the following output:
    //       0061 0062 0063 0064 0000 0041 0042 0043 0044 0000
    //       0061 0062 0063 0064
    

    String(SByte*)String(SByte*, Int32, Int32) Так как конструкторы интерпретируются value с помощью кодовой страницы ANSI по умолчанию, вызов этих конструкторов с идентичными массивами байтов может создавать строки, имеющие разные значения в разных системах.

Обработка повторяющихся строк

Приложения, которые анализируют или декодируют потоки текста, часто используют String(Char[], Int32, Int32) конструктор или StringBuilder.Append(Char[], Int32, Int32) метод для преобразования последовательностей символов в строку. Повторное создание новых строк с одинаковым значением вместо создания и повторного использования памяти одной строки. Если вы, скорее всего, создадите одно строковое значение многократно путем вызова конструктора String(Char[], Int32, Int32) , даже если заранее не знаете, какие одинаковые строковые значения могут быть, можно использовать таблицу подстановки.

Например, предположим, что вы считываете и анализируете поток символов из файла, содержащего XML-теги и атрибуты. При анализе потока часто возникают определенные маркеры (т. е. последовательности символов с символьным значением). Маркеры, эквивалентные строкам "0", "1", "true" и "false", скорее всего, часто происходят в XML-потоке.

Вместо преобразования каждого токена в новую строку можно создать System.Xml.NameTable объект для хранения часто возникающих строк. Объект NameTable улучшает производительность, так как он извлекает сохраненные строки без выделения временной памяти. При обнаружении маркера используйте NameTable.Get(Char[], Int32, Int32) метод для получения маркера из таблицы. Если токен существует, метод возвращает соответствующую строку. Если маркер не существует, используйте NameTable.Add(Char[], Int32, Int32) метод для вставки маркера в таблицу и получения соответствующей строки.

Пример 1. Использование назначения строк

В следующем примере создается новая строка, назначая ей строковый литерал. Он создает вторую строку, назначив ему значение первой строки. Это два наиболее распространенных способа создания экземпляра нового String объекта.

using System;

public class Example3
{
   public static void Main()
   {
      String value1 = "This is a string.";
      String value2 = value1;
      Console.WriteLine(value1);
      Console.WriteLine(value2);
   }
}
// The example displays the following output:
//    This is a string.
//    This is a string.
let value1 = "This is a string."
let value2 = value1
printfn "%s" value1
printfn "%s" value2
// The example displays the following output:
//    This is a string.
//    This is a string.
Module Example
   Public Sub Main()
      Dim value1 As String = "This is a string."
      Dim value2 As String = value1
      Console.WriteLine(value1)
      Console.WriteLine(value2)
   End Sub
End Module
' The example displays the following output:
'    This is a string.
'    This is a string.

Пример 2. Использование массива символов

В следующем примере показано, как создать объект String из массива символов.

// Unicode Mathematical operators
char [] charArr1 = {'\u2200','\u2202','\u200F','\u2205'};
String szMathSymbols = new String(charArr1);

// Unicode Letterlike Symbols
char [] charArr2 = {'\u2111','\u2118','\u2122','\u2126'};
String szLetterLike = new String (charArr2);

// Compare Strings - the result is false
Console.WriteLine("The Strings are equal? " +
    (String.Compare(szMathSymbols, szLetterLike)==0?"true":"false") );
// Unicode Mathematical operators
let charArr1 = [| '\u2200'; '\u2202'; '\u200F'; '\u2205' |]
let szMathSymbols = String charArr1

// Unicode Letterlike Symbols
let charArr2 = [| '\u2111'; '\u2118'; '\u2122'; '\u2126' |]
let szLetterLike = String charArr2

// Compare Strings - the result is false
printfn $"The Strings are equal? %b{String.Compare(szMathSymbols, szLetterLike) = 0}"
' Unicode Mathematical operators
Dim charArr1() As Char = {ChrW(&H2200), ChrW(&H2202), _
                          ChrW(&H200F), ChrW(&H2205)}
Dim szMathSymbols As New String(charArr1)

' Unicode Letterlike Symbols
Dim charArr2() As Char = {ChrW(&H2111), ChrW(&H2118), _
                          ChrW(&H2122), ChrW(&H2126)}
Dim szLetterLike As New String(charArr2)

' Compare Strings - the result is false
Console.WriteLine("The strings are equal? " & _
        CStr(szMathSymbols.Equals(szLetterLike)))

Пример 3. Использование части массива символов и повторение одного символа

В следующем примере показано, как создать объект из части массива символов и как создать новый StringString объект, содержащий несколько вхождения одного символа.

// Create a Unicode String with 5 Greek Alpha characters
String szGreekAlpha = new String('\u0391',5);
// Create a Unicode String with a Greek Omega character
String szGreekOmega = new String(new char [] {'\u03A9','\u03A9','\u03A9'},2,1);

String szGreekLetters = String.Concat(szGreekOmega, szGreekAlpha, szGreekOmega.Clone());

// Examine the result
Console.WriteLine(szGreekLetters);

// The first index of Alpha
int ialpha = szGreekLetters.IndexOf('\u0391');
// The last index of Omega
int iomega = szGreekLetters.LastIndexOf('\u03A9');

Console.WriteLine("The Greek letter Alpha first appears at index " + ialpha +
    " and Omega last appears at index " + iomega + " in this String.");
// Create a Unicode String with 5 Greek Alpha characters
let szGreekAlpha = String('\u0391',5)
// Create a Unicode String with a Greek Omega character
let szGreekOmega = String([| '\u03A9'; '\u03A9'; '\u03A9' |],2,1)

let szGreekLetters = String.Concat(szGreekOmega, szGreekAlpha, szGreekOmega.Clone())

// Examine the result
printfn $"{szGreekLetters}"

// The first index of Alpha
let ialpha = szGreekLetters.IndexOf '\u0391'
// The last index of Omega
let iomega = szGreekLetters.LastIndexOf '\u03A9'

printfn $"The Greek letter Alpha first appears at index {ialpha} and Omega last appears at index {iomega} in this String."
' Create a Unicode String with 5 Greek Alpha characters
Dim szGreekAlpha As New String(ChrW(&H0391), 5)
' Create a Unicode String with a Greek Omega character
Dim szGreekOmega As New String(New Char() {ChrW(&H03A9), ChrW(&H03A9), _
                                           ChrW(&H03A9)}, 2, 1)

Dim szGreekLetters As String = String.Concat(szGreekOmega, szGreekAlpha, _
                                             szGreekOmega.Clone())

' Examine the result
Console.WriteLine(szGreekLetters)

' The first index of Alpha
Dim iAlpha As Integer = szGreekLetters.IndexOf(ChrW(&H0391))
' The last index of Omega
Dim iomega As Integer = szGreekLetters.LastIndexOf(ChrW(&H03A9))

Console.WriteLine("The Greek letter Alpha first appears at index {0}.", _ 
                  ialpha)
Console.WriteLIne("The Greek letter Omega last appears at index {0}.", _
                  iomega)

Пример 4. Использование указателя на массив символов

В следующем примере показано, как создать объект String из указателя на массив символов. Пример C# должен быть скомпилирован с помощью переключателя компилятора /unsafe .

using System;

public class Example4
{
   public static unsafe void Main()
   {
      char[] characters = { 'H', 'e', 'l', 'l', 'o', ' ', 
                            'w', 'o', 'r', 'l', 'd', '!', '\u0000' };
      string value;
      
      fixed (char* charPtr = characters) {
         value = new String(charPtr);
      }                            
      Console.WriteLine(value);
   }
}
// The example displays the following output:
//        Hello world!
#nowarn "9"
open System

let characters = 
    [| 'H'; 'e'; 'l'; 'l'; 'o'; ' ' 
       'w'; 'o'; 'r'; 'l'; 'd'; '!'; '\u0000' |]

let value =
    use charPtr = fixed characters
    String charPtr

printfn $"{value}"
// The example displays the following output:
//        Hello world!

Пример 5. Создание экземпляра строки из указателя и диапазона массива

В следующем примере рассматриваются элементы массива символов в течение периода или восклицательного знака. Если он найден, он создает экземпляр строки из символов в массиве, предшествующих символам препинания. Если нет, он создает экземпляр строки со всем содержимым массива. Пример C# должен быть скомпилирован с помощью переключателя компилятора /unsafe .

using System;

public class Example1
{
   public static unsafe void Main()
   {
      char[] characters = { 'H', 'e', 'l', 'l', 'o', ' ', 
                            'w', 'o', 'r', 'l', 'd', '!', '\u0000' };
      String value;
      
      fixed (char* charPtr = characters) {
         int length = 0;
         Char* iterator = charPtr;
   
         while (*iterator != '\x0000')
         {
            if (*iterator == '!' || *iterator == '.')
               break;
            iterator++;
            length++;
         }
         value = new String(charPtr, 0, length);
      }
      Console.WriteLine(value);
   }
}
// The example displays the following output:
//      Hello World
#nowarn "9"
open System
open FSharp.NativeInterop

let characters = 
    [| 'H'; 'e'; 'l'; 'l'; 'o'; ' '
       'w'; 'o'; 'r'; 'l'; 'd'; '!'; '\u0000' |]

[<EntryPoint>]
let main _ =
    use charPtr = fixed characters
    let mutable length = 0
    let mutable iterator = charPtr
    let mutable broken = false
    while not broken && NativePtr.read iterator <> '\u0000' do
        if NativePtr.read iterator = '!' || NativePtr.read iterator = '.' then
            broken <- true
        else
            iterator <- NativePtr.add iterator 1
            length <- length + 1
    String(charPtr, 0, length)
    |> printfn "%s"
    0
// The example displays the following output:
//      Hello World

Пример 6. Создание экземпляра строки из указателя на подписанный массив байтов

В следующем примере показано, как создать экземпляр String класса с помощью конструктора String(SByte*) .

unsafe
{
    // Null terminated ASCII characters in an sbyte array
    String szAsciiUpper = null;
    sbyte[] sbArr1 = new sbyte[] { 0x41, 0x42, 0x43, 0x00 };
    // Instruct the Garbage Collector not to move the memory
    fixed(sbyte* pAsciiUpper = sbArr1)
    {
        szAsciiUpper = new String(pAsciiUpper);
    }
    String szAsciiLower = null;
    sbyte[] sbArr2 = { 0x61, 0x62, 0x63, 0x00 };
    // Instruct the Garbage Collector not to move the memory
    fixed(sbyte* pAsciiLower = sbArr2)
    {
        szAsciiLower = new String(pAsciiLower, 0, sbArr2.Length);
    }
    // Prints "ABC abc"
    Console.WriteLine(szAsciiUpper + " " + szAsciiLower);

    // Compare Strings - the result is true
    Console.WriteLine("The Strings are equal when capitalized ? " +
        (String.Compare(szAsciiUpper.ToUpper(), szAsciiLower.ToUpper())==0?"true":"false") );

    // This is the effective equivalent of another Compare method, which ignores case
    Console.WriteLine("The Strings are equal when capitalized ? " +
        (String.Compare(szAsciiUpper, szAsciiLower, true)==0?"true":"false") );
}
// Null terminated ASCII characters in an sbyte array
let szAsciiUpper =
    let sbArr1 = [| 0x41y; 0x42y; 0x43y; 0x00y |]
    // Instruct the Garbage Collector not to move the memory
    use pAsciiUpper = fixed sbArr1
    String pAsciiUpper

let szAsciiLower =
    let sbArr2 = [| 0x61y; 0x62y; 0x63y; 0x00y |]
    // Instruct the Garbage Collector not to move the memory
    use pAsciiLower = fixed sbArr2 
    String(pAsciiLower, 0, sbArr2.Length)

// Prints "ABC abc"
printfn $"{szAsciiUpper} {szAsciiLower}"

// Compare Strings - the result is true
printfn $"The Strings are equal when capitalized ? %b{String.Compare(szAsciiUpper.ToUpper(), szAsciiLower.ToUpper()) = 0}"

// This is the effective equivalent of another Compare method, which ignores case
printfn $"The Strings are equal when capitalized ? %b{String.Compare(szAsciiUpper, szAsciiLower, true) = 0}"