static void Main(string[] args) {
string userIsDone = "нет";
// Проверка на соответствие строке в нижнем регистре.
while(userIsDone.ToLower() != "да") {
Console.Write("Вы удовлетворены? [да] [нет]: ");
userIsDone = Console.ReadLine();
Console.WriteLine{"В цикле while");
}
}
Цикл do/while подобен циклу while. Как и цикл while, цикл do/while используется для выполнения последовательности действий неопределенное число раз. Разница в том, что цикл do/while гарантирует выполнение соответствующего блока программного кода как минимум один раз (простой цикл while может не выполниться ни разу, если условие его окончания окажется неверным с самого начала).
static void Main(string[] args) {
string userlsDone = "";
do {
Console.WriteLine("В цикле do/while");
Console.Write("Вы удовлетворены? [да] [нет]: ");
userIsDone = Console.ReadLine();
} while(userIsDone.ToLower() != "да"); // Обратите внимание на точку с запятой!
}
Конструкции выбора решений и операции сравнения
В C# определяются две простые конструкции, позволяющие изменить поток выполнения программы по набору условий:
• оператор if/else;
• оператор switch.
В отличие от C и C++, оператор if/else в C# может работать только с булевыми выражениями, а не с произвольными значениями -1, 0. Поэтому в операторах if/else обычно используются операции C#, показанные в табл. 3.6. чтобы получить буквальные булевы значения.
Таблица 3.6. Операции сравнения в C#
Операция сравнения Пример использования Описание == if (age == 30) Возвращает true (истина) только в том случае, когда выражении одинаковы != if("Foo"!= myStr) Возвращает true (истина) только в том случае, когда выражения различны ‹ › ‹= ›= if(bonus‹2000) if(bonus›2000) if(bonus‹=2000) if(bonus›=2000) Возвращает true (истина) только в том случае, когда выражение А соответственно меньше, больше, меньше или равно, больше или равно выражению В
Программистам, использующим C и C++, следует обратить внимание на то, что их привычные приемы по проверке условий "на равенство нулю" в C# работать не будут. Например, вы хотите выяснить, будет ли данная строка длиннее пустой строки. Может возникнуть искушение написать следующее.
// В C# это недопустимо, поскольку Length возвращает int, а не bool.
string thoughtOfThеDay = "Старую coбaку новым трюкам научить МОЖНО";
if (thoughtOfTheDay.Length) {
…
}
В данном случае для использования cвойства String.Length нужно изменить условие так, как показано ниже.
// Это допустимо, так как результатом будет true или false.
if (0 != thoughtOfTheDay.Length)
Чтобы обеспечить более сложную проверку, оператор if может содержать сложные выражения и другие операторы, Синтаксис C# в данном случае идентичен C(++) и Java (и не слишком отличается от Visual Basic). Для построения сложных выражений C# имеет вполне отвечающий ожиданиям набор условных операций, описания которых предлагаются в табл. 3.7.
Таблица 3.7. Условные операции в C#
Операция Пример Описание && if ((age == 30)&& (name == "Fred")) Условная операция AND (И) || if ((age == 30) || (name == "Fred")) Условная операция OR (ИЛИ) ! if (!myBool) Условная операция NOT (HE)
Другой простой конструкцией выбора, предлагаемой в C#, является оператор switch. Как и в других языках типа C, оператор switch позволяет обработать поток выполнения программы на основе заданного набора вариантов. Например, следующий метод Main() позволяет печатать строку, зависящую от выбранного варианта (случай default предназначен для обработки непредусмотренных вариантов выбора).
// Переключение по числовому значению.
static void Main(string[] args) {
Console.WriteLine("1 [C#], 2 [VB]");
Console.Write("Выберите язык, который вы предпочитаете: ");
string langChoice = Console.ReadLine();
int n = int.Parse(langChoice);
switch (n) {
case 1:
Console.WriteLine("Отлично! C# – это прекрасный язык.");
break;
case 2:
Console.WriteLine("VB .NET: ООП, многозадачность и т.д.!");
break;
default:
Console.WriteLine("Хорошо… удачи вам с таким выбором!");
break;
}
}
Замечание. В C# требуется, чтобы каждый вариант выбора (включая default), содержащий выполняемые операторы, завершался оператором break или goto, во избежание прохода сквозь структуру при невыполнении условия.
Приятной особенностью оператора switch в C# является то, что в рамках этого оператора вы можете оценивать не только числовые, но и строковые данные. Именно это используется в следующем операторе switch (при таком подходе нет необходимости переводить пользовательские данные в числовые значения).
static void Main(string[] args) {
Console.WriteLine("C# или VB");
Console.Write("Выберите язык, который вы предпочитаете: ");
string langChoice = Console.ReadLine();
switch (langChoice) {
case "C#":
Console.WriteLine("Отлично! C# – это прекрасный язык. ");
break;
case "VB":
Console.WriteLine("VB .NET: ООП, многозадачность и т.д.!");
break;
default:
Console.WriteLine("Хорошо… удачи вам с таким выбором!");
break;
}
}
Исходный код. Проект IterationsAndDeсisions размещен в подкаталоге, соответствующем главе 3.
Типы, характеризуемые значениями, и ссылочные типы
Подобно любому другому языку программирования, язык C# определяет ряд ключевых слов, представляющих базовые типы данных, такие как целые числа, символьные данные, числа с плавающим десятичным разделителем и логические (булевы) значения. Если вы работали с языком C++, то будете рады узнать, что здесь эти внутренние типы являются "фиксированными константами", т.е., например, после создания элемента целочисленных данных все языки .NET будут понимать природу этого типа и диапазон его значений,
Тип данных .NET может либо характеризоваться значением, либо быть ссылочным типом (т.е. характеризоваться ссылкой). К типам, характеризуемым значением, относятся все числовые типы данных (int, float и т.д.), а также перечни и структуры, размещаемые в стеке. Поэтому типы, характеризуемые значениями, можно сразу же удалить из памяти, как только они оказываются вне контекста их определений.
// Целочисленные данные характеризуются значением!
public void SomeMethod() {
int i = 0;
Console.WriteLine(i);
} // здесь 'i' удаляется из стека.
Когда вы присваиваете один характеризуемый значением тип другому, по умолчанию выполняется "почленное" копирование. Для числовых и булевых типов данных единственным "членом", подлежащим копированию, является непосредственное значение переменной,
// Для типов, характеризуемых значениями, в результате такого
// присваивания в стек помещаются две независимые переменные.
public void SomeMethod() {
int i = 99;
int j = i;
// После следующего присваивания значением 'i' останется 99.
j = 8732;
}
Этот пример может не содержать для вас ничего нового, но важно понять, что .NET-структуры (как и перечни, которые будут рассмотрены в этой главе позже) тоже являются типами, характеризуемыми значением. Структуры, в частности, дают возможность использовать основные преимущества объектно-ориентированного подхода (инкапсуляции) при сохранении эффективности размещения данных в стеке. Подобно классам, структуры могут использовать конструкторы (с аргументами) и определять любое число членов.
Все структуры неявно получаются из класса System.ValueType. С точки зрения функциональности, единственной целью System.ValueType является "переопределение" виртуальных методов System.Object (этот объект будет описан чуть позже) с целью учета особенностей семантики типов, заданных значениями, в противоположность ссылочным типам. Методы экземпляра, определенные с помощью System.ValueType, будут идентичны соответствующим методам System.Object.