Суббота, 18.05.2024, 17:13
Приветствую Вас Гость | RSS
Форма входа
Поиск
Календарь
«  Май 2024  »
ПнВтСрЧтПтСбВс
  12345
6789101112
13141516171819
20212223242526
2728293031
Статистика

Онлайн всего: 1
Гостей: 1
Пользователей: 0

Charles Petzold ".NET Book Zero" Глава 5. Строки и консоль (I часть)

В предыдущей главе, аргумент, передаваемый в метод Console.WriteLine выглядел следующим образом:

"Hello, Microsoft .NET Framework"

Этот аргумент, больше известен как – буквенная строка. Которая состоит из группы символов, ограниченных двойными кавычками. Это Unicode символы, что означает, что каждый символ представлен 16-битным числом. (Более подробную информацию о Unicode можно найти на www.unicode.org.)

Как и в C и C++, обратная косая черта интерпретируется как символ ESC-последовательности, и как символ, который следует обрабатывается особым образом. Это позволяет встраивать специфические знаки в строку символов, а иначе это было бы невозможно. В следующей таблице показаны поддерживаемые управляющие последовательности и их Unicode эквиваленты в шестнадцатеричном формате.

Я никогда не видел необходимости в строке предварять одинарные кавычки с помощью обратной косой черты. (Вы должны будете делать это при определении литеру потому, что одиночные символы ограничиваются одинарными кавычками.) Последняя запись в таблице показывает, как вы можете вставлять произвольные символы Unicode в символьную строку. Символы ABCD означают любое 4-значное шестнадцатеричное число. Например:

"Hello, Microsoft\x00AE .NET Framework"

Теперь слово "Microsoft" сопровождается символом ® (это осчастливит адвокатов). Тем не менее, консоль не поддерживает символы, отличные от ASCII так же хорошо, поэтому, если вы на самом деле сделать это изменение в программе из предыдущей главы, консоль, вероятно, покажет просто как строчную "r". Если вы действительно, очень-очень хотите, чтобы ваши программы отображали ® символ, вам придется отказаться от консоли и написать небольшую программу Forms Windows. Windows Forms является клиентской платформа для Windows поддерживающейся во всех версиях .NET.

TextWithUnicodeChar.cs
//----------------------------------------------------
// TextWithUnicodeChar.cs (c) 2006 by Charles Petzold
//----------------------------------------------------
using System.Windows.Forms;
 
class TextWithUnicodeChar
{
    public static void Main()
    {
       MessageBox.Show("Hello, Microsoft\x00AE .NET Framework");
    }
}

Show – это статический метод в классе MessageBox, который находится в пространстве имён System.Windows.Forms. Без директивы using, вы должны вызвать этот метод по его ужасающему полному имени:

System.Windows.Forms.MessageBox.Show(
    "Hello, Microsoft\x00AE .NET Framework");

Windows Forms классы находятся в сборке System.Windows.Forms, которая находится в файле System.Windows.Forms.dll. Для компиляции этой программы, вам понадобится ссылка на эту сборку. В Visual Studio в обозревателе решений щелкните правой кнопкой мыши Ссылки (References), а затем Добавить ссылку (Add Reference). Или выберите Add Reference из меню Project. В диалоговом окне Добавить ссылку (Add Reference) выберите вкладку .NET и сборку System.Windows.Forms. При компиляции в командной строке, используйте переключатель /r, чтобы указать другие сборки.

Метод MessageBox.Show выводит окно сообщения Windows, с кнопкой OK. При нажатии на кнопку OK, окно сообщения исчезнет с экрана, MessageBox.Show вернется, и программа завершается.

Хотя программа Windows Forms корректно отображает ® символ, имейте в виду, что не каждый шрифт поддерживает все символы Unicode.

Вы также можете использовать управляющие последовательности Unicode в именах переменных. См. "Спецификацию языка C#", § 2.4.1 для более подробной информации).

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

"\\Documents and Settings\\Charles\\Favorites"

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

@"\Documents and Settings\Charles\Favorites"

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

@"The symbol \ is called a ""backslash"""

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

В отличие от C и C++, C# поддерживает тип данных string для хранения строк. Внутри такого метода, как Main, Вы можете объявить переменную типа string используя объявление:

string str;

Все переменные должны быть объявлены до их использования. Имена переменных обычно начинают с буквы или символа подчеркивания, они также могут содержать цифры, но правила о том, какие символы Unicode допускаются в имени переменной являются довольно сложными. (См Спецификацию языка C #, §2.4.2). Конечно имя переменной не должно начинаться с букв str, но я люблю так делать, потому что это напоминает мне о том, что это строковая переменная.

При желании вы можете инициализировать эту строку, при объявлении переменной:

string str = "This is an initialized string";

Или вы можете установить значение строки при помощи выражения присваивания, после того как переменная была объявлена:

string str;
str = "This is an assigned string";

Нет никакой разницы между инициализацией строки в операторе объявления и присваиванием значения сразу же после оператора объявления.

Вы можете объявить несколько строковых переменных в одном объявлении, разделяя их запятыми:

string str1, str2, str3;

Вы можете инициализировать все или некоторые из этих переменных:

string str1, str2 = "initialized", str3;

Пока строковой переменной не будет присвоено значение, она считается не инициализированной, и компилятор C#  не позволит использовать эту переменную. Вот неправильная последовательность операторов:

string str;
Console.WriteLine(str);

C# будет жаловаться на "Использование локальной переменной "str", которой не присвоено значение".

Вы можете установить переменную string пустую строку:

string str = "";

Или вы можете установить переменную строку в нуль ключевым словом – null:

string str = null;

В любом случае, переменная на данный момент считается инициализированной, двумя совершенно разными путями. В первом случае, переменная str ссылается на строку, которая не имеет никаких символов. Во втором случае, считается что переменная str имеет нулевую ссылку, которая означает, что он она ни на что не ссылается. В любом случае, Console.WriteLine отобразит только пустоту для этой строки.

Далее мы приводим полную программу, которая использует инициализированную строку в методе Main:

class Program
{
    public static void Main()
    {
        string strDisplay = "Hello, Microsoft .NET Framework";
        System.Console.WriteLine(strDisplay);
    }
}

Строковая переменная должна быть объявлена и установлена, перед её использованием. Вот неправильный код:

class Program
{
    public static void Main()
    {
        System.Console.WriteLine(strDisplay);
        string strDisplay = "Hello, Microsoft .NET Framework";
    }
}

Вы получите сообщение об ошибке компилятора гласящее, что: "Невозможно использовать локальную переменную "strDisplay" до ее объявления".

Этот код тоже неправильный:

class Program
{
    public static void Main()
    {
        string strDisplay;
        System.Console.WriteLine(strDisplay);
        strDisplay = "Hello, Microsoft .NET Framework";
    }
}

Переменная объявлена, но она не инициализируется во время вызова WriteLine. Сообщение об ошибке компилятора будет таким – "Использование локальной переменной "strDisplay", которой не присвоено значение"

Переменная strDisplay является – локальной переменной, поскольку она объявлена в методе (в данном случае в методе Main), и данная переменная видна только внутри этого метода. Вы также можете объявить переменную за пределами метода Main, но внутри класса:

class Program
{
    static string strDisplay = "Hello, Microsoft .NET Framework";
 
    public static void Main()
    {
        System.Console.WriteLine(strDisplay);
    }
}

Переменная strDisplay теперь является полем, и потенциально доступна для любого метода в классе Program. И strDisplay, и метод Main считаются членами класса. Обратите внимание, что strDisplay объявлена как статическая переменная, то есть она является частью самого класса, а не экземпляра класса. Программа может обращаться к strDisplay предварив его именем класса:

System.Console.WriteLine(Program.strDisplay);

Не имеет значения, где внутри класса объявлено поле strDisplay. Следующий код будет прекрасно работать:

class Program
{
    public static void Main()
    {
        System.Console.WriteLine(strDisplay);
    }
 
    static string strDisplay = "Hello, Microsoft .NET Framework";
}

Это выглядит немного странным, потому что в контексте целого класса strDisplay объявлен после того, как используется, но это правило применяется только к локальным переменным. И Main, и strDisplay являются членами класса, а порядок членов, как правило, не имеет значения. (Однако, если одно поле устанавливается значением другого поля, то порядок имеет значение).

Вы также можете объявить поле, а его значение установить в методе:

class Program
{
    public static void Main()
    {
        strDisplay = "Hello, Microsoft .NET Framework";
        System.Console.WriteLine(strDisplay);
    }
 
    static string strDisplay;
}

Если вы оставите оператор присваивания в Main, программа будет компилироваться и работать нормально, но ничего не будет отображаться. Если поля явно не инициализированы, они всегда неявно инициализируются нулевыми значениями. Строковое поле (и другие типы ссылок) инициализируются значением null.

Но вы не можете осуществлять присваивание вне методов. Следующий код не скомпилируется:

class Program
{
    static string strDisplay;
    strDisplay = "Hello, Microsoft .NET Framework";
 
    public static void Main()
    {
        System.Console.WriteLine(strDisplay);
    }
}

Сообщение компилятора об ошибке: "Недопустимая лексема "=" в объявлении класса, структуры или интерфейса" – означает, что, когда C# компилятор разбирал программу, все было ОК, пока он не добрался до знака равенства.

Вы можете использовать одно и то же имя для полей и для локальных переменных:

class Program
{
    static string strDisplay = "Это поле.";
 
    static void Main()
    {
        string strDisplay = "Это локальная переменная.";
        System.Console.WriteLine(strDisplay);
    }
}

Внутри метода Main, локальная переменная имеет больший приоритет и программа отобразит "Это локальная переменная." Однако, поскольку поле, кажется, не служат никакой цели в этой программе, компилятор C# выдаст предупреждающее сообщение, которое гласит: "Полю "Program.strDisplay" присвоено значение, но оно ни разу не использовано".

Следующий пример показывает, как вы можете получить доступ к полю, а не к локальной переменной:

class Program
{
    static string strDisplay = "Это поле.";
 
    static void Main()
    {
        string strDisplay = "Это локальная переменная.";
        System.Console.WriteLine(Program.strDisplay);
    }
}

Обратите внимание, что strDisplay теперь начинаются с имени класса в вызове WriteLine. Программа отображает "Это поле," но компилятор теперь выдаёт предупреждающее сообщение, что: "Переменной "strDisplay" присвоено значение, но оно ни разу не использовано".

Если вы посмотрите в документацию для класса Console, и, в частности для метода WriteLine, вы найдете много разных версий этого метода. Тот, которым мы неявно пользуемся, определяется следующим образом (в синтаксисе C#):

public static void WriteLine(string value)

Этот метод отображает строку, переданную в качестве аргумента, а затем переходит на следующую строку. Ключевое слово void означает, что метод ничего не возвращает при вызове. При дальнейшем изучении класса Console, вы найдете метод, называющийся Write, и версию метода Write определённую следующим образом:

public static void Write(string value)

Метод Write отображает свой аргумент, но не переходите на следующую строку. Также, есть версия WriteLine, которая не делает ничего, кроме перехода к следующей строке:

public static void WriteLine()

В документации нет версии Write без параметров, потому что она вообще ничего не будет делать. Вы можете переписать основную часть FirstProgram, примерно, следующим образом:

Console.Write("Hello, ");
Console.Write("Microsoft ");
Console.Write(".NET ");
Console.Write("Framework!");
Console.WriteLine();

Обратите внимание, что первые три строки заканчиваются пробелом так, что слова по-прежнему остаются разделёнными. Если вы посмотрите дальше документацию Console, вы откроете для себя метод, называемый ReadLine:

public static string ReadLine()

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

GetTheUsersName.cs
//------------------------------------------------
// GetTheUsersName.cs (c) 2006 by Charles Petzold
//------------------------------------------------
using System;
 
class GetTheUsersName
{
    static void Main()
    {
        Console.Write("Type your name and press Enter: ");
        string strName = Console.ReadLine();
        Console.Write("Your name is ");
        Console.WriteLine(strName);
    }
}

Обратите внимание на то, как вызов первого метода Console.Write используется для отображения строки. Курсор не переходит на новую строку, а отображается через один пробел после двоеточия. Console.ReadLine вызывает эхо введенных символов в консоли, но не возвращает значение, до тех пор, пока пользователь не нажмёт Enter, что также заставляет курсор перейти на следующую строку. Сочетание Console.Write и Console.WriteLine, после этого, выводит информацию в одной строке.

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

Console.WriteLine("Your name is " + strName);

Строковый литерал должен появиться на одной строке (за исключением буквальных строк, которые могут переходить на несколько строк), поэтому оператор конкатенации является хорошим способом объединить строки, которые являются слишком длинными, для того чтобы умещаться на одной строке.

Limerick.cs

//-----------------------------------------
// Limerick.cs (c) 2006 by Charles Petzold
//-----------------------------------------
using System;
class Limerick
{
    static void Main()
    {
        string strLimerick =
            "There once was a coder named Otto\r\n" +
            "Who had a peculiar motto:\r\n" +
            "    \"The goto is king,\r\n" +
            "    To thee I sing!\"\r\n" +
            "Maybe that's why he's often quite blotto.\r\n";
        Console.WriteLine(strLimerick);
    }
}

Обратите внимание на количество управляющих последовательностей для встроенных двойных кавычек в третьей и четвертой строках, а также, на то, что пять строк заканчиваются управляющей последовательностью для возврата каретки и перевода строк, которые обычно находятся в конце строк в MS-DOS и Windows. Потому что последняя линия имеет возврат каретки и перевод строки, то и вся строка отображается при помощи Console.WriteLine, пустая строка появится в конце результата Лимерик.

В документации класса Console, методы: Write, WriteLine, и ReadLine – все находятся в разделе под названием "Методы" (Methods). Вы также увидите раздел, обозначенный "Свойства" (Properties). Если у вас есть установленная SDK для .NET Framework 1.0 или 1.1, вы увидите только несколько пунктов под этим заголовком. Для версий 2.0 и выше, тем не менее, вы увидите их намного больше. Давайте рассмотрим некоторые из этих пунктов.

Вот, например, свойство под именем Title, в документации по C#, с синтаксисом:

public static string Title { get; set; }

Подобно методам в Console, это свойство является открытым, что означает, что мы можем получить доступ к свойству Title извне класса Console, например, в одной из наших программ. Это свойство статическое, а это значит, что мы будем на самом деле ссылаться на него, как на Console.Title. Каждое свойство имеет тип, а тип свойства Titlestring. В фигурных скобках появляются слова get и set. Это означает, что свойство может читать ("получать") и устанавливать значение. Когда вы будете писать свои собственные свойства (которые я рассмотрю в главе 17), вы увидите, каким образом эти слова get и set фигурируют в определении свойства.

Свойство Console.Title – "читаемо", что означает, что вы можете получить и использовать значение свойства, подобно этому:

string strTitle = Console.Title;

Или, вы можете поместить Console.Title внутрь WriteLine, чтобы отобразить значение свойства:

Console.WriteLine(Console.Title);

Если вы поместите этот код в верхней части Limerick.cs, он покажет то же самое название, которое отображается в строке заголовка окна консоли, в которой программа Limerick работает.

Свойство Title является – "записываемым", что означает, что вы можете поместить следующее выражение в Limerick.cs:

Console.Title = "Limerick";

Это название появится в заголовке окна консоли. (Тем не менее, если вы компилируете и запускаете программу из командной строки, название будет изменено только во время выполнения программы, а это очень короткое время. Вы можете поместить вызов Console.ReadLine в нижней части программы, чтобы действительно увидеть, что новое название оказало эффект.)

Как вы можете видеть, синтаксис участвующий в получении и установке свойства Title делает его похожим на поле. Но не всё так просто. Хотя свойства, несомненно, похожи на поля при использовании, свойства на самом деле реализованы с дополнительным кодом. Дополнительный код выполняется, когда вы получаете или устанавливаете свойство.

Если вставить операторы для доступа и изменения Title в программу Limerick, а затем вы посмотрите на исполняемый файл через IL дизассемблер, вы увидите, что Title волшебным образом превратилось в методы, вызывающие get_Title и set_Title. Хотя свойства имеют синтаксис полей, они реализуются в классе Console, как методы.

Класс Console также определяет свойства с именами BackgroundColor и ForegroundColor. Эти два свойства также являются "читаемым" и "записываемым", но тип этих свойств ConsoleColor. Что же это за ConsoleColor? Если вы посмотрите чуть дальше, в документации пространства имен System, вы несомненно увидите страницу под названием "ConsoleColor – перечисление".

ConsoleColor является перечислением, которое означает, что оно имеет набор членов, которые ассоциированы с целыми числами. В C#, перечисления являются строго типизированными, и член перечисления должен начинаться с имени перечисления. Вот так можно установить передний и задний цвет фона в программе:

Console.BackgroundColor = ConsoleColor.Yellow;
Console.ForegroundColor = ConsoleColor.Blue;

Использование перечислений в C# требует большего набора кода, чем вы можете быть привыкли, но зато – меньше путаницы или ошибок.

Если вы поместите объявления этих Background и Foreground в верхнюю часть Limerick.cs, результат будет не очень привлекательным, потому что, с этими новыми красками будут показаны, только отображаемые программой символы. После установки цвета, вы, вероятно, захотите очистить экран консоли вызвав статический метод:

Console.Clear();

Файл Limerick.cs завершает каждую строку с символов '\r' и '\n', которые обозначают возврат каретки и перевод строки. '\r' – возвращает курсор в начало строки, а '\n' вызывает следующую строку, чтобы начать запись в новой строке. Как вы знаете, символы перевода строки варьируются в зависимости от платформы операционной системы, и, если вы действительно хотите чтобы ваша программа достигла независимости от платформы, вы можете подумать об использовании статического свойства Environment.NewLine. Это статическое свойство NewLine в классе Environment, который также является частью пространства имен System. Это свойство должно зависеть от конкретной среды, на которой запущена программа.

Свойство Environment.NewLine имеет в документации по C# следующий синтаксис:

public static string NewLine { get; }

Тип свойства – string, но его можно только получить. Вы не можете установить свойство Environment.NewLine. Его можно использовать в Limerick.cs следующим образом:

"There once was a coder named Otto" + Environment.NewLine +

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

string NL = Environment.NewLine;

Или вы можете определить NL в поле, если вам нужна реализация не только одним способом. В любом случае вы можете обращаться к NL в конце каждой строки:

"There once was a coder named Otto" + NL +

Намного лучше!

C# также определяет тип char для хранения одного 16-битного знака Unicode. Но строго говоря, char не является числовым типом, как и в C и C++. В C# нет такого понятия, как, char со знаком или без знаковый char. Литера задаётся одинарными кавычками, и все управляющие последовательности, показанные ранее в этой главе, так же являются действительными:

char chBackSlash = '\\';

Вы можете объединять текстовые переменные со строковыми переменными или текстовыми строками:

string strDirectory = "C:" + chBackSlash + "Windows";

Вы также можете объединять строки и целые числа. Вот пример:

Console.WriteLine("Schubert was " + 31 + " when he died.");

Это будет отображено как:

Schubert was 31 when he died.

То, что происходит за кулисами этого, на самом деле немного сложнее, чем вы можете себе представить, но я пока не хочу раскрывать этот секрет.

Как и в C и C++, основным целым типом данных в C# является Int. Вы можете объявить и инициализировать Int следующим образом:

int age = 31;

Вы можете объединить эту переменную со строкой:

Console.WriteLine("Schubert was " + age + " when he died.");

Результат будет таким же, как и раньше. Вы можете попытаться выполнить вычисления напрямую в выражении Console.WriteLine используя годы, в которые Шуберт родился и умер:

Console.WriteLine("Schubert was " + 1828 – 1797 + " when he died.");

Это не сработает. C# (как C и C++) оценивает аддитивные операторы, такие как плюс и минус – слева направо. Первый знак плюс вызывает число 1828, которое сцепляется со строкой "Шуберт" и в результате будет – "Шуберт 1828". Далее идёт знак минус и, я считаю, что это будет проблемой, потому что теперь у нас есть строка минус число.

Нам поможет простое заключение вычислений в скобки:

Console.WriteLine("Schubert was " + (1828 – 1797) + " when he died.");

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

Console.WriteLine(31 + " when he died Schubert was.");

Следующее выражение, тоже будет работоспособным:

Console.WriteLine(1828 – 1797 + " when he died Schubert was.");

Как в C и C++, стандартным типом данных с плавающей точкой в C# является – double. Вот объявление и инициализация double, плюс выражение, которое отображает значение:

double onethird = 1 / 3.0;
Console.WriteLine("One divided by three equals " + onethird);

Как вы, наверное, заметили, выражение, устанавливающее в переменною одну треть – написано не как – 1 разделенное на 3. Как в C и C++, C# интерпретирует числовые литералы без десятичных знаков – как целые числа, поэтому деление целых выполняется с усечением, так что результат будет равен нулю. Выражение одного из двух чисел с плавающей точкой в буквальном виде, вызывает преобразование другого в число с плавающей точкой для разделения. Оператор WriteLine выдаст:

One divided by three equals 0.333333333333333

Класс Math, в пространстве имён System, содержит набор статических методов, которые выполняют основные логарифмические и тригонометрические вычисления. Класс Math также содержит два постоянных поля, названные PI и E, имеющих тип double. Вот выражение использующее Math.PI:

Console.WriteLine("A circle's circumference divided by its diameter is " +
Math.PI);

Это выражение покажет:

A circle's circumference divided by its diameter is 3.14159265358979

Я уверен, что некоторые программисты хотят знать: как C# хранит строки? Завершаются ли строки нулевым символов, как это происходит в C и C++, или что-то другое? Ответ будет: Или что-то другое!

Ключевое слово string в C# – на самом деле псевдоним для класса в пространстве имен System с именем String. Обратите внимание на разницу в следующих случаях: Ключевое слово C# –  string, а класс – String. В любой C# программе, вы можете заменить слово string на System.String и программа останется точно такой же:

System.String str = "Just a string";

Если у вас есть директива using для пространства имен System, вы можете заменить string на String и использовать две взаимозаменяемые формы:

String str = "Another string";

Но, вы не сможете этого сделать обратившись к классу System.string (обратите внимание на нижний регистр string):

System.string str = "Not a workable string"; // Won’t work!

Потому что string – это псевдонимом System.String, которое транслировалось бы в System.System.String, а такого – не существует.

Кроме того, типа данных char – псевдоним для структуры System.Char, тип данных int – псевдоним для структуры System.Int32, а double – это псевдоним System.Double. Как указывает Джеффри Рихтер ("CLR via C#", стр 119), это так – как если бы каждая C# программа содержала следующие using директивы:

using string = System.String;
using char = System.Char;
using int = System.Int32;
using double = System.Double;

И так далее. (Существуют ещё основные типы данных, помимо этих четырёх типов.)

Более глубоко перефразируя это: Любую переменную string можно назвать "объектом типа String," или "экземпляром класса String". Да и сам класс String предоставляет множество положительных вещей. Когда вы изучите документацию класса String вы откроете для себя много методов с говорящими именами: Substring, LastIndexOf, ToLower, ToUpper, и многое другое. Все эти методы производят различные манипуляции со строками.

Класс String также имеет два важных свойства. Свойство Length определённое следующим образом:

public int Length { get; }

Это свойство имеет тип Int, и доступно только для "чтения". Но, большим отличием, по сравнению с другими свойствами, которые вы видели до сих пор, является отсутствие ключевого слова static. Length – не статическое свойство класса String. Length, это наоборот – свойство экземпляра, что означает, что оно относится к конкретной строковой переменной, а не к классу String. По большому счёту, свойства экземпляра (и методы экземпляра) встречаются гораздо чаще, чем статические свойства и статические методы – на столько, что "свойства" и "методы" являются свойством экземпляра по умолчанию. Вам ненужно предварять свойство Length именем класса String.

Вы не можете использовать выражение String.Length. Что бы это, такое, могло означать? Вместо этого, используйте свойство Length с экземпляром класса String – который мы непреднамеренно называли строковой переменной:

string strMyString = "This is my string";
Console.WriteLine(strMyString.Length);

Выражение strMyString.Length возвращает длину строки, в данном случае – число 17.

Length не возвращает информацию о дозаторе мороженного, которым является класс String.Length измеряет размер отдельного мороженного.

Вы также можете применить свойство Length к буквальной строке:

"This is a string literal".Length

Это выражение имеет значение – 24.

Класс String имеет два свойства, одно из которых, кажется, называется Chars. И действительно, в некоторых языках программирования вы действительно можете использовать свойство с таким именем. Тем не менее, в C#, в представленной декларации свойства Chars, вы увидите следующее:

public char this [int index] { get; }

Слово Chars нигде не появляется в этом заявлении. Вместо этого мы видим свойство, которое имеет имя "this" и свойство типа char. Но this на самом деле это ключевое слово C#, и в данном контексте оно является специальным синтаксисом. Это заявление определяет индексатор для класса String, которое указывает, что вы можете использовать квадратные скобки для индекса строковой переменной и получать этот символ. Например, выражение:

strMyString[0]

возвращает первый символ strMyString, который, как было задано ранее, является символом 'T'. Синтаксис такой же, как у индексов C или C++ массивов (и на самом деле такой же, как индекс в массиве C#). Индексация начинается с 0, поэтому в выражении strMyString [5] – это 6-й символ в строке, или "i". Вы также можете индексировать строковые литералы:

"This is a string literal"[15]

Этот символ будет – 'G'. Индекс может находиться в диапазоне от 0 до на один меньше, чем свойство строки Length. Далее мы приводим небольшую программу, которая демонстрирует Length и свойства индексов.

StringProperties.cs

//-------------------------------------------------
// StringProperties.cs (c) 2006 by Charles Petzold
//-------------------------------------------------
using System;
 
class StringProperties
{
    static void Main()
    {
        Console.Write("Enter some text: ");
        string strEntered = Console.ReadLine();
        Console.WriteLine();
        Console.WriteLine("The text you entered has " +
            strEntered.Length + " characters");
        Console.WriteLine("The first character is " +
            strEntered[0]);
        Console.WriteLine("The last character is " +
            strEntered[strEntered.Length - 1]);
        Console.WriteLine();
    }
}

Конечно, поскольку вы человек любознательный, вы захотите, посмотреть, что произойдёт, когда вы нажмёте клавишу Enter, не вводя вообще какого-либо текста. Метод Console.ReadLine возвращает пустую строку в этом случае, свойство Length пустой строки равно 0, но первый индекс, который находится в strEntered[0], имеет небольшую проблему, так как – нет никакого символа для возвращения. Вы, вероятно, получите диалоговое окно, информирующее вас о проблеме, а затем какой-нибудь текст в окне консоли, который начинается так:

Необработанное исключение: System.IndexOutOfRangeException:
Индекс находился вне границ массива.

В этом сообщении будет так же отображаться полный путь до файла StringProperties.cs, и номер строки, где произошла проблема. Номер строки может быть точным, если вы компилируете в режиме отладки, или это может относиться только к методу, в котором оно произошло.

В любом случае, программа будет корректно завершена. Зависания, или отображения кучи кракозябр на экране, или сбоя Windows вместе с программой – не произойдёт. Обратите внимание, что сообщение говорит – "Неизвестное исключение", что означает, что здесь есть возможность для вас, программистов, написать код, который будет обрабатывать эту проблему без прекращения программы. (Вы узнаете, как обрабатывать исключения в главе 12.) IndexOutOfRangeException является классом в пространстве имен System, и это только один из нескольких классов, для различного рода исключений, с которыми программа может столкнуться. Я буду использовать имена этих классов для обозначения общих исключений.

Сообщение об исключении указывает, что "Индекс находится вне границ массива", и это может заставить вас задаться вопросом: Является ли string, на самом деле, просто массивом символов? Определенно, хотя и не в том же смысле, как в С и С++, строки являются массивами символов. Основное отличие заключается в определении индексатора:

public char this [int index] { get; }

Это свойство – только для чтения. Код, подобный следующему, просто не допустим:

strMyString[5] = 'a'; // Can't do it!

После того, как строка создаётся, вы не можете изменять отдельные символы. Не существует какого-либо метода в классе String, который может изменить символы строки. Строка является неизменной.

Что это значит? Ну, это, конечно, не означает, полный запрет на присваивание строковой переменной другой строки. Такой код является совершенно законным:

string str = "first string";
str = "second string";

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

Поскольку строки являются неизменными, некоторые обычные операции из С и С++ больше не возможны в C#. Например, Microsoft C и C ++ включают в себя библиотечную функцию называющуюся _strupr, которая преобразует строку в верхний регистр. В программе C, если pMyCString является указателем на символ или массив char, то вам нужно использовать _strupr так:

_strupr(pMyCString);

Функция _strupr берет каждый символ в pMyCString и преобразует его в верхний регистр и сохраняет его обратно в том же месте. Функция _strupr возвращает указатель на преобразованную строку, но это – тот же самый указатель, переданный функции.

Эквивалентным методом класса String является – ToUpper. Но, для экземпляра строки с именем strMyCSharpString, вы не можете просто вызвать метод следующим образом:

strMyCSharpString.ToUpper(); // Won’t do anything!

Синтаксически это утверждение справедливо, но оно не окажет никакого влияния на переменную strMyCSharpString. Строки являются неизменными и, следовательно, буквы strMyCSharpString не могут быть изменены. Метод ToUpper на самом деле создаёт новую строку. Вы должны присвоить строковой переменной возвращаемое значение ToUpper:

string strMyUpperCaseString = strMyCSharpString.ToUpper();

Или вы могли бы присвоить возвращаемое значение той же строковой переменной:

strMyCSharpString = strMyCSharpString.ToUpper();

Во втором случае, оригинальная строка (которая содержит строчные буквы) по-прежнему существует, но, поскольку на неё, скорее всего, больше нет ссылок из какого-либо места программы, то она становится целью для сборщика мусора.

Итак, предположим, у вас есть строка, определённая следующим образом:

string str = "abcdifg";

и вы хотите изменить пятый символ на «ё». Вы знаете, что не можете сделать это следующим образом:

str[4] = 'e'; // Won't work!

Индекс работает только на получение. Итак, как вы это сделаете? Есть пара возможных подходов, которые вы можете накопать при помощи поиска в документации String. Вызов метода:

str = str.Replace('i', 'e');

возвращает строку, в которой все вхождения 'i' были заменены на 'ё'. Обратите внимание, значение, возвращаемое Replace присваивается тот же переменной, которая вызвала метод. Кроме того, вы можете сначала вызвать Remove, чтобы создать новую строку с удаленным одним или несколькими символами, по указанному индексу с заданным количеством. Например, вызов:

str = str.Remove(4, 1);

удаляет один символ в четвертой позиции ('i'). Затем можно вызвать Insert, чтобы вставить новую строку, которая в данном случае является одним символом:

str = str.Insert(4, "e");

Или вы можете делать оба действия в одном объявлении:

str = str.Remove(4, 1).Insert(4, "e");

Несмотря на использование одной переменной строки с именем str, два вызываемых метода в последней инструкции создадут две дополнительные строки, и "е" в кавычках будет еще одной строкой.

Или вы можете составить новую строку из подстрок:

str = str.Substring(0, 4) + "e" + str.Substring(5);

Теперь, просмотрев документацию класса String, вы наверняка заметите, что она включает в себя раздел, обозначенный как "конструкторы String". Конструкторы служат для создания и инициализации объектов. При использовании конструктор требует ключевое слово new с последующим названием самого класса и возможными аргументами в скобках. Далее, мы приводим объявление строковой переменной, которая использует один из конструкторов, определенных в классе String:

string strAtSigns = new string('@', 15);

Вы можете использовать либо строчное ключевое слово string или класс String с заглавной буквы, при вызове конструктора. Данный специфический конструктор определён при помощи char, в качестве первого параметра, и int – в качестве второго параметра. Он создаёт строку, содержащую 15 вхождений символа @, которые формируют достаточно красивый браслетик:

@@@@@@@@@@@@@@@

© Charles Petzold ".NET Book Zero" 2007