Архитектура Аудит Военная наука Иностранные языки Медицина Металлургия Метрология
Образование Политология Производство Психология Стандартизация Технологии


Последнее обновление: 09.03.2017



Язык C# и платформа .NET

Последнее обновление: 09.03.2017

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

По сравнению с другими языками C# достаточно молодой, но в то же время он уже прошел большой путь. Первая версия языка вышла вместе с релизом Microsoft Visual Studio .NET в феврале 2002 года. Текущей версией языка является версия C# 7.0, которая вышла в 7 марта 2017 года вместе с Visual Studio 2017.

C# является языком с Си-подобным синтаксисом и близок в этом отношении к C++ и Java. Поэтому, если вы знакомы с одним из этих языков, то овладеть C# будет легче.

C# является объектно-ориентированным и в этом плане много перенял у Java и С++. Например, C# поддерживает полиморфизм, наследование, перегрузку операторов, статическую типизацию. Объектно-ориентированный подход позволяет решить задачи по построению крупных, но в тоже время гибких, масштабируемых и расширяемых приложений. И C# продолжает активно развиваться, и с каждой новой версией появляется все больше интересных функциональностей, как, например, лямбды, динамическое связывание, асинхронные методы и т.д.

Роль платформы .NET

Когда говорят C#, нередко имеют в виду технологии платформы .NET (WPF, ASP.NET). И, наоборот, когда говорят .NET, нередко имеют в виду C#. Однако, хотя эти понятия связаны, отождествлять их неверно. Язык C# был создан специально для работы с фреймворком .NET, однако само понятие .NET несколько шире.

Как-то Билл Гейтс сказал, что платформа .NET - это лучшее, что создала компания Microsoft. Возможно, он был прав. Фреймворк .NET представляет мощную платформу для создания приложений. Можно выделить следующие ее основные черты:

  • Поддержка нескольких языков. Основой платформы является общеязыковая среда исполнения Common Language Runtime (CLR), благодаря чему .NET поддерживает несколько языков: наряду с C# это также VB.NET, C++, F#, а также различные диалекты других языков, привязанные к .NET, например, Delphi.NET. При компиляции код на любом из этих языков компилируется в сборку на общем языке CIL (Common Intermediate Language) - своего рода ассемблер платформы .NET. Поэтому мы можем сделать отдельные модули одного приложения на отдельных языках.
  • Кроссплатформенность. .NET является переносимой платформой (с некоторыми ограничениями). Например, последняя версия платформы на данный момент .NET Framework поддерживается на большинстве современных ОС Windows (Windows 10/8.1/8/7/Vista). А благодаря проекту Mono можно создавать приложения, которые будут работать и на других ОС семейства Linux, в том числе на мобильных платформах Android и iOS.
  • Мощная библиотека классов. .NET представляет единую для всех поддерживаемых языков библиотеку классов. И какое бы приложение мы не собирались писать на C# - текстовый редактор, чат или сложный веб-сайт - так или иначе мы задействуем библиотеку классов .NET.
  • Разнообразие технологий. Общеязыковая среда исполнения CLR и базовая библиотека классов являются основой для целого стека технологий, которые разработчики могут задействовать при построении тех или иных приложений. Например, для работы с базами данных в этом стеке технологий предназначена технология ADO.NET. Для построения графических приложений с богатым насыщенным интерфейсом - технология WPF. Для создания веб-сайтов - ASP.NET и т.д.

Также еще следует отметить такую особенность языка C# и фреймворка .NET, как автоматическая сборка мусора. А это значит, что нам в большинстве случаев не придется, в отличие от С++, заботиться об освобождении памяти. Вышеупомянутая общеязыковая среда CLR сама вызовет сборщик мусора и очистит память.

JIT-компиляция

Как выше писалось, код на C# компилируется в приложения или сборки с расширениями exe или dll на языке CIL. Далее при запуске на выполнение подобного приложения происходит JIT-компиляция (Just-In-Time) в машинный код, который затем выполняется. При этом, поскольку наше приложение может быть большим и содержать кучу инструкций, в текущий момент времени будет компилироваться лишь та часть приложения, к которой непосредственно идет обращение. Если мы обратимся к другой части кода, то она будет скомпилирована из CIL в машинный код. При том уже скомпилированная часть приложения сохраняется до завершения работы программы. В итоге это повышает производительность.

По сути это все, что вкратце надо знать о платформе .NET. А теперь создадим первое приложение.

Установка версии языка

Переменные

Литералы

Логические литералы

Есть две логических константы - true (истина) и false (ложь):

1 2 Console.WriteLine(true); Console.WriteLine(false);

Целочисленные литералы

Целочисленные литералы представляют положительные и отрицательные целые числа, например, 1, 2, 3, 4, -7, -109. Целочисленные литералы могут быть выражены в десятичной, шестнадцатеричной и двоичной форме.

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

1 2 3 Console.WriteLine(-11); Console.WriteLine(5); Console.WriteLine(505);

Числа в двоичной форме предваряются символами 0b, после которых идет набор из нулей и единиц:

1 2 3 Console.WriteLine(0b11); // 3 Console.WriteLine(0b1011); // 11 Console.WriteLine(0b100001); // 33

Для записи числа в шестнадцатеричной форме применяются символы 0x, после которых идет набор символов от 0 до 9 и от A до F, которые собственно представляют число:

1 2 3 Console.WriteLine(0x0A); // 10 Console.WriteLine(0xFF); // 255 Console.WriteLine(0xA1); // 161

Вещественные литералы

Вещественные литералы представляют вещественные числа. Этот тип литералов имеет две формы. Первая форма - вещественные числа с фиксированной запятой, при которой дробную часть отделяется от целой части точкой. Например:

1 2 3 3.14 100.001 -0.38

Также вещественные литералы могут определяться в экспоненциальной форме MEp, где M — мантисса, E - экспонента, которая фактически означает "*10^" (умножить на десять в степени), а p — порядок. Например:

1 2 Console.WriteLine(3.2e3); // по сути равно 3.2 * 10<sup>3</sup> = 3200 Console.WriteLine(1.2E-1); // равно 1.2 * 10<sup>-1</sup> = 0.12

Символьные литералы

Символьные литералы представляют одиночные символы. Символы заключаются в одинарные кавычки.

Символьные литералы бывают нескольких видов. Прежде всего это обычные символы:

1 2 3 '2' 'A' 'T'

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

  • '\n' - перевод строки
  • '\t' - табуляция
  • '\' - обратный слеш

И если компилятор встретит в тексте последовательность \t, то он будет воспринимать эту последовательность не как слеш и букву t, а как табуляцию - то есть длинный отступ.

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

Еще один способ определения символов представляет использования шестнадцатеричных кодов ASCII. Для этого в одинарных кавычках указываются символы '\x', после которых идет шестнадцатеричный код символа из таблицы ASCII. Коды символов из таблицы ASCII можно посмотреть здесь.

Например, литерал '\x78' представляет символ "x":

1 2 Console.WriteLine('\x78'); // x Console.WriteLine('\x5A'); // Z

И последний способ определения символьных литералов представляет применение кодов из таблицы символов Unicode. Для этого в одинарных кавычках указываются символы '\u', после которых идет шестнадцатеричный код Unicode. Например, код '\u0411' представляет кириллический символ 'Б':

1 2 Console.WriteLine('\u0420'); // Р Console.WriteLine('\u0421'); // С

Строковые литералы

Строковые литералы представляют строки. Строки заключаются в двойные кавычки:

1 2 3 Console.WriteLine("hello"); Console.WriteLine("фыва"); Console.WriteLine("hello word");

Если внутри строки необходимо вывести двойную кавычку, то такая внутренняя кавычка предваряется обратным слешем:

1 Console.WriteLine("Компания \"Рога и копыта\"");

Также в строках можно использовать управляющие последовательности. Например, последовательность '\n' осуществляет перевод на новую строку:

1 Console.WriteLine("Привет \nмир");

При выводе на консоль слово "мир" будет перенесено на новую строку:

Привет

мир

Null

null представляет ссылку, которая не указывает ни на какой объект. То есть по сути отсутствие значения.

Типы данных

Использование суффиксов

При присвоении значений надо иметь в виду следующую тонкость: все вещественные литералы рассматриваются как значения типа double. И чтобы указать, что дробное число представляет тип float или тип decimal, необходимо к литералу добавлять суффикс: F/f - для float и M/m - для decimal.

1 2 3 4 5 float a = 3.14F; float b = 30.6f;   decimal c = 1005.8M; decimal d = 334.8m;

Подобным образом все целочисленные литералы рассматриваются как значения типа int. Чтобы явным образом указать, что целочисленный литерал представляет значение типа uint, надо использовать суффикс U/u, для типа long - суффикс L/l, а для типа ulong - суффикс UL/ul:

1 2 3 uint a = 10U; long b = 20L; ulong c = 30UL;

Неявная типизация

Ранее мы явным образом указывали тип переменных, например, int x;. И компилятор при запуске уже знал, что x хранит целочисленное значение.

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

1 2 3 4 5 var hello = "Hell to World"; var c = 20;   Console.WriteLine(c.GetType().ToString()); Console.WriteLine(hello.GetType().ToString());

Для неявной типизации вместо названия типа данных используется ключевое слово var. Затем уже при компиляции компилятор сам выводит тип данных исходя из присвоенного значения. В примере выше использовалось выражение Console.WriteLine(c.GetType().ToString());, которое позволяет нам узнать выведенный тип переменной с. Так как по умолчанию все целочисленные значения рассматриваются как значения типа int, то поэтому в итоге переменная c будет иметь тип int или System.Int32

Эти переменные подобны обычным, однако они имеют некоторые ограничения.

Во-первых, мы не можем сначала объявить неявно типизируемую переменную, а затем инициализировать:

1 2 3 4 5 6 7 // этот код работает int a; a = 20;   // этот код не работает var c; c= 20;

Во-вторых, мы не можем указать в качестве значения неявно типизируемой переменной null:

1 2 // этот код не работает var c=null;

Так как значение null, то компилятор не сможет вывести тип данных.

Double или decimal

Из выше перечисленного списка типов данных очевидно, что если мы хотим использовать в программе числа до 256, то для их хранения мы можем использоват переменные типа byte. При использовании больших значений мы можем взять тип short, int, long. То же самое для дробных чисел - для обычных дробных чисел можно взять тип float, для очень больших дробных чисел - тип double. Тип decimal здесь стоит особняком в том плане, что несмотря на большую разрядность по сравнению с типом double, тип double может хранить большее значение. Однако значение decimal может содержать до 28-29 знаков после запятой, тогда как значение типа double - 15-16 знаков после запятой.

Decimal чаще находит применение в финансовых вычислениях, тогда как double - в математических операциях. Общие различия между этими двумя типами можно выразить следующей таблицей:

  Decimal Double
Наибольшее значение ~1028 ~10308
Наименьшее значение (без учета нуля) 10-28 ~10-323
Знаков после запятой 28-29 15-16
Разрядность 16 байт 8 байт
Операций в секунду сотни миллионов миллиарды

 

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

Бинарные арифметические операции:

  • +

Операция сложения двух чисел:

1 2 int x = 10; int z = x + 12; // 22
  • -

Операция вычитания двух чисел:

1 2 int x = 10; int z = x - 6; // 4
  • *

Операция умножения двух чисел:

1 2 int x = 10; int z = x * 5; // 50
  • /

операция деления двух чисел:

1 2 3 4 5 6 int x = 10; int z = x / 5; // 2   double a = 10; double b = 3; double c = a / b; // 3.33333333

При делении стоит учитывать, что если оба операнда представляют целые числа, то результат также будет округляться до целого числа:

1 double z = 10 / 4; //результат равен 2

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

Для выхода из этой ситуации необходимо определять литералы или переменные, участвующие в операции, именно как типы double или float:

1 double z = 10.0 / 4.0; //результат равен 2.5
  • %

Операция получение остатка от целочисленного деления двух чисел:

1 2 double x = 10.0; double z = x % 4.0; //результат равен 2

Также есть ряд унарных операций, в которых принимает участие один операнд:

  • ++

Операция инкремента

Инкремент бывает префиксным: ++x - сначала значение переменной x увеличивается на 1, а потом ее значение возвращается в качестве результата операции.

И также существует постфиксный инкремент: x++ - сначала значение переменной x возвращается в качестве результата операции, а затем к нему прибавляется 1.

1 2 3 4 5 6 7 int x1 = 5; int z1 = ++x1; // z1=6; x1=6 Console.WriteLine($"{x1} - {z1}");   int x2 = 5; int z2 = x2++; // z2=5; x2=6 Console.WriteLine($"{x2} - {z2}");
  • --

Операция декремента или уменьшения значения на единицу. Также существует префиксная форма декремента (--x) и постфиксная (x--).

1 2 3 4 5 6 7 int x1 = 5; int z1 = --x1; // z1=4; x1=4 Console.WriteLine($"{x1} - {z1}");   int x2 = 5; int z2 = x2--; // z2=5; x2=4 Console.WriteLine($"{x2} - {z2}");

При выполнении сразу нескольких арифметических операций следует учитывать порядок их выполнения. Приоритет операций от наивысшего к низшему:

  1. Инкремент, декремент
  2. Умножение, деление, получение остатка
  3. Сложение, вычитание

Для изменения порядка следования операций применяются скобки.

Рассмотрим набор операций:

1 2 3 4 5 int a = 3; int b = 5; int c = 40; int d = c---b*a; // a=3 b=5 c=39 d=25 Console.WriteLine($"a={a} b={b} c={c} d={d}");

Здесь мы имеем дело с тремя операциями: декремент, вычитание и умножение. Сначала выполняется декремент переменной c, затем умножение b*a, и в конце вычитание. То есть фактически набор операций выглядел так:

1 int d = (c--)-(b*a);

Но с помощью скобок мы могли бы изменить порядок операций, например, следующим образом:

1 2 3 4 5 int a = 3; int b = 5; int c = 40; int d = (c-(--b))*a; // a=3 b=4 c=40 d=108 Console.WriteLine($"a={a} b={b} c={c} d={d}");

 

Поразрядные операции

Логические операции

  • &(логическое умножение)

Умножение производится поразрядно, и если у обоих операндов значения разрядов равно 1, то операция возвращает 1, иначе возвращается число 0. Например:

1 2 3 4 5 6 7 int x1 = 2; //010 int y1 = 5;//101 Console.WriteLine(x1&y1); // выведет 0   int x2 = 4; //100 int y2 = 5; //101 Console.WriteLine(x2 & y2); // выведет 4

В первом случае у нас два числа 2 и 5. 2 в двоичном виде представляет число 010, а 5 - 101. Поразрядно умножим числа (0*1, 1*0, 0*1) и в итоге получим 000.

Во втором случае у нас вместо двойки число 4, у которого в первом разряде 1, так же как и у числа 5, поэтому в итоге получим (1*1, 0*0, 0 *1) = 100, то есть число 4 в десятичном формате.

  • | (логическое сложение)

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

1 2 3 4 5 6 int x1 = 2; //010 int y1 = 5;//101 Console.WriteLine(x1|y1); // выведет 7 - 111 int x2 = 4; //100 int y2 = 5;//101 Console.WriteLine(x2 | y2); // выведет 5 - 101
  • ^ (логическое исключающее ИЛИ)

Также эту операцию называют XOR, нередко ее применяют для простого шифрования:

1 2 3 4 5 6 7 int x = 45; // Значение, которое надо зашифровать - в двоичной форме 101101 int key = 102; //Пусть это будет ключ - в двоичной форме 1100110 int encrypt = x ^ key; //Результатом будет число 1001011 или 75 Console.WriteLine("Зашифрованное число: " +encrypt);   int decrypt = encrypt ^ key; // Результатом будет исходное число 45 Console.WriteLine("Расшифрованное число: " + decrypt);

Здесь опять же производятся поразрядные операции. Если у нас значения текущего разряда у обоих чисел разные, то возвращается 1, иначе возвращается 0. Таким образом, мы получаем из 9^5 в качестве результата число 12. И чтобы расшифровать число, мы применяем ту же операцию к результату.

  • ~ (логическое отрицание или инверсия)

Еще одна поразрядная операция, которая инвертирует все разряды: если значение разряда равно 1, то оно становится равным нулю, и наоборот.

1 2 int x = 9; Console.WriteLine(~x);

Операции сдвига

Операции сдвига также производятся над разрядами чисел. Сдвиг может происходить вправо и влево.

  • x<<y - сдвигает число x влево на y разрядов. Например, 4<<1 сдвигает число 4 (которое в двоичном представлении 100) на один разряд влево, то есть в итоге получается 1000 или число 8 в десятичном представлении.
  • x>>y - сдвигает число x вправо на y разрядов. Например, 16>>1 сдвигает число 16 (которое в двоичном представлении 10000) на один разряд вправо, то есть в итоге получается 1000 или число 8 в десятичном представлении.

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

Операции присваивания

Операции сравнения

В операциях сравнения сравниваются два операнда и возвращается значение типа bool - true, если выражение верно, и false, если выражение неверно.

  • ==

Сравнивает два операнда на равенство. Если они равны, то операция возвращает true, если не равны, то возвращается false:

1 2 3 int a = 10; int b = 4; bool c = a == b; // false
  • !=

Сравнивает два операнда и возвращает true, если операнды не равны, и false, если они равны.

1 2 3 4 int a = 10; int b = 4; bool c = a != b; // true bool d = a!=10; // false
  • <

Операция "меньше чем". Возвращает true, если первый операнд меньше второго, и false, если первый операнд больше второго:

1 2 3 int a = 10; int b = 4; bool c = a < b; // false
  • >

Операция "больше чем". Сравнивает два операнда и возвращает true, если первый операнд больше второго, иначе возвращает false:

1 2 3 4 int a = 10; int b = 4; bool c = a > b; // true bool d = a > 25; // false
  • <=

Операция "меньше или равно". Сравнивает два операнда и возвращает true, если первый операнд меньше или равен второму. Иначе возвращает false.

1 2 3 4 int a = 10; int b = 4; bool c = a <= b; // false bool d = a <= 25; // true
  • >=

Операция "больше или равно". Сравнивает два операнда и возвращает true, если первый операнд больше или равен второму, иначе возвращается false:

1 2 3 4 int a = 10; int b = 4; bool c = a >= b; // true bool d = a >= 25; // false

Логические операции

Также в C# определены логические операторы, которые также возвращают значение типа bool. В качестве операндов они принимают значения типа bool. Как правило, применяются к отношениям и объединяют несколько операций сравнения.

  • |

Операция логического сложения или логическое ИЛИ. Возвращает true, если хотя бы один из операндов возвращает true.

1 2 bool x1 = (5 > 6) | (4 < 6); // 5 > 6 - false, 4 < 6 - true, поэтому возвращается true bool x2 = (5 > 6) | (4 > 6); // 5 > 6 - false, 4 > 6 - false, поэтому возвращается false
  • &

Операция логического умножения или логическое И. Возвращает true, если оба операнда одновременно равны true.

1 2 bool x1 = (5 > 6) & (4 < 6); // 5 > 6 - false, 4 < 6 - true, поэтому возвращается false bool x2 = (5 < 6) & (4 < 6); // 5 < 6 - true, 4 < 6 - true, поэтому возвращается true
  • ||

Операция логического сложения. Возвращает true, если хотя бы один из операндов возвращает true.

1 2 bool x1 = (5 > 6) || (4 < 6); // 5 > 6 - false, 4 < 6 - true, поэтому возвращается true bool x2 = (5 > 6) || (4 > 6); // 5 > 6 - false, 4 > 6 - false, поэтому возвращается false
  • &&

Операция логического умножения. Возвращает true, если оба операнда одновременно равны true.

1 2 bool x1 = (5 > 6) && (4 < 6); // 5 > 6 - false, 4 < 6 - true, поэтому возвращается false bool x2 = (5 < 6) && (4 < 6); // 5 > 6 - true, 4 > 6 - true, поэтому возвращается truse
  • !

Операция логического отрицания. Производится над одним операндом и возвращает true, если операнд равен false. Если операнд равен true, то операция возвращает false:

1 2 bool a = true; bool b = !a; // false
  • ^

Операция исключающего ИЛИ. Возвращает true, если либо первый, либо второй операнд (но не одновременно) равны true, иначе возвращает false

1 2 bool x5 = (5 > 6) ^ (4 < 6); // 5 > 6 - false, 4 < 6 - true, поэтому возвращается true bool x6 = (50 > 6) ^ (4 / 2 < 3); // 50 > 6 - true, 4/2 < 3 - true, поэтому возвращается false

Здесь у нас две пары операций | и || (а также & и &&) выполняют похожие действия, однако же они не равнозначны.

В выражении z=x|y; будут вычисляться оба значения - x и y.

В выражении же z=x||y; сначала будет вычисляться значение x, и если оно равно true, то вычисление значения y уже смысла не имеет, так как у нас в любом случае уже z будет равно true. Значение y будет вычисляться только в том случае, если x равно false

То же самое касается пары операций &/&&. В выражении z=x&y; будут вычисляться оба значения - x и y.

В выражении же z=x&&y; сначала будет вычисляться значение x, и если оно равно false, то вычисление значения y уже смысла не имеет, так как у нас в любом случае уже z будет равно false. Значение y будет вычисляться только в том случае, если x равно true

Поэтому операции || и && более удобны в вычислениях, так как позволяют сократить время на вычисление значения выражения, и тем самым повышают производительность. А операции | и & бУсловные конструкции

Конструкция if/else

Конструкция if/else проверяет истинность некоторого условия и в зависимости от результатов проверки выполняет определенный код:

1 2 3 4 5 6 int num1 = 8; int num2 = 6; if(num1 > num2) { Console.WriteLine($"Число {num1} больше числа {num2}"); }

После ключевого слова if ставится условие. И если это условие выполняется, то срабатывает код, который помещен далее в блоке if после фигурных скобок. В качестве условий выступают ранее рассмотренные операции сравнения.

В данном случае у нас первое число больше второго, поэтому выражение num1 > num2истинно и возвращает true, следовательно, управление переходит к строке Console.WriteLine("Число {num1} больше числа {num2}");

Но что, если мы захотим, чтобы при несоблюдении условия также выполнялись какие-либо действия? В этом случае мы можем добавить блок else:

1 2 3 4 5 6 7 8 9 10 int num1 = 8; int num2 = 6; if(num1 > num2) { Console.WriteLine($"Число {num1} больше числа {num2}"); } else { Console.WriteLine($"Число {num1} меньше числа {num2}"); }

Но при сравнении чисел мы можем насчитать три состояния: первое число больше второго, первое число меньше второго и числа равны. Используя конструкцию else if, мы можем обрабатывать дополнительные условия:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 int num1 = 8; int num2 = 6; if(num1 > num2) { Console.WriteLine($"Число {num1} больше числа {num2}"); } else if (num1 < num2) { Console.WriteLine($"Число {num1} меньше числа {num2}"); } else { Console.WriteLine("Число num1 равно числу num2"); }

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

1 2 3 4 5 6 int num1 = 8; int num2 = 6; if(num1 > num2 && num1==8) { Console.WriteLine($"Число {num1} больше числа {num2}"); }

В данном случае блок if будет выполняться, если num1 > num2 равно true и num1==8равно true.

Конструкция switch

Конструкция switch/case аналогична конструкции if/else, так как позволяет обработать сразу несколько условий:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 Console.WriteLine("Нажмите Y или N"); string selection = Console.ReadLine(); switch (selection) { case "Y": Console.WriteLine("Вы нажали букву Y"); break; case "N": Console.WriteLine("Вы нажали букву N"); break; default: Console.WriteLine("Вы нажали неизвестную букву"); break; }

После ключевого слова switch в скобках идет сравниваемое выражение. Значение этого выражения последовательно сравнивается со значениями, помещенными после оператора сase. И если совпадение будет найдено, то будет выполняться определенный блок сase.

В конце каждого блока сase должен ставиться один из операторов перехода: break, goto case, return или throw. Как правило, используется оператор break. При его применении другие блоки case выполняться не будут.

Однако если мы хотим, чтобы, наоборот, после выполнения текущего блока case выполнялся другой блок case, то мы можем использовать вместо break оператор goto case:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 int number = 1; switch (number) { case 1: Console.WriteLine("case 1"); goto case 5; // переход к case 5 case 3: Console.WriteLine("case 3"); break; case 5: Console.WriteLine("case 5"); break; default: Console.WriteLine("default"); break; }

Если мы хотим также обработать ситуацию, когда совпадения не будет найдено, то можно добавить блок default, как в примере выше.

Применение оператора return позволит выйти не только из блока case, но и из вызывающего метода. То есть, если в методе Main после конструкции switch..case, в которой используется оператор return, идут какие-либо операторы и выражения, то они выполняться не будут, а метод Main завершит работу.

Оператор throw применяется для выброса ошибок и будет рассмотрен в одной из следующим тем.

Тернарная операция

Тернарную операция имеет следующий синтаксис: [первый операнд - условие] ? [второй операнд] : [третий операнд]. Здесь сразу три операнда. В зависимости от условия тернарная операция возвращает второй или третий операнд: если условие равно true, то возвращается второй операнд; если условие равно false, то третий. Например:

1 2 3 4 5 6 7 int x=3; int y=2; Console.WriteLine("Нажмите + или -"); string selection = Console.ReadLine();   int z = selection=="+"? (x+y) : (x-y); Console.WriteLine(z);

Здесь результатом тернарной операции является переменная z. Если мы выше вводим "+", то z будет равно второму операнду - (x+y). Иначе z будет равно третьему операнду.

 

ольше подходят для выполнения поразрядных операций над числами.

Циклы

Цикл for

Цикл for имеет следующее формальное определение:

1 2 3 4 for ([инициализация счетчика]; [условие]; [изменение счетчика]) { // действия }

Рассмотрим стандартный цикл for:

1 2 3 4 for (int i = 0; i < 9; i++) { Console.WriteLine($"Квадрат числа {i} равен {i*i}"); }

Первая часть объявления цикла - int i = 0 - создает и инициализирует счетчик i. Счетчик необязательно должен представлять тип int. Это может быть и другой числовой тип, например, float. И перед выполнением цикла его значение будет равно 0. В данном случае это то же самое, что и объявление переменной.

Вторая часть - условие, при котором будет выполняться цикл. Пока условное выражение возвращает true, будет выполняться цикл. В данном случае цикл будет выполняться, пока счетчик i не достигнет 9.

И третья часть - приращение счетчика на единицу. Опять же нам необязательно увеличивать на единицу. Можно уменьшать: i--.

В итоге блок цикла сработает 9 раз, пока значение i не станет равным 9. И каждый раз это значение будет увеличиваться на 1.

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

1 2 3 4 5 int i = 0; for (; ;) { Console.WriteLine($"Квадрат числа {++i} равен {i * i}"); }

Формально определение цикла осталось тем же, только теперь блоки в определении у нас пустые: for (; i <;). У нас нет инициализированной переменной-счетчика, нет условия, поэтому цикл будет работать вечно - бесконечный цикл.

Мы также можем опустить ряд блоков:

1 2 3 4 5 int i = 0; for (; i<9;) { Console.WriteLine($"Квадрат числа {++i} равен {i * i}"); }

Этот пример по сути эквивалентен первому примеру: у нас также есть счетчик, только создан он вне цикла. У нас есть условие выполнения цикла. И есть приращение счетчика уже в самом блоке for.

Цикл do

В цикле do сначала выполняется код цикла, а потом происходит проверка условия в инструкции while. И пока это условие истинно, цикл повторяется. Например:

1 2 3 4 5 6 7 int i = 6; do { Console.WriteLine(i); i--; } while (i > 0);

Здесь код цикла сработает 6 раз, пока i не станет равным нулю. Но важно отметить, что цикл do гарантирует хотя бы единократное выполнение действий, даже если условие в инструкции while не будет истинно. То есть мы можем написать:

1 2 3 4 5 6 7 int i = -1; do { Console.WriteLine(i); i--; } while (i > 0);

Хотя у нас переменная i меньше 0, цикл все равно один раз выполнится.

Цикл while

В отличие от цикла do цикл while сразу проверяет истинность некоторого условия, и если условие истинно, то код цикла выполняется:

1 2 3 4 5 6 int i = 6; while (i > 0) { Console.WriteLine(i); i--; }

Операторы continue и break

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

Например:

1 2 3 4 5 6 for (int i = 0; i < 9; i++) { if (i == 5) break; Console.WriteLine(i); }

Хотя в условии цикла сказано, что цикл будет выполняться, пока счетчик i не достигнет значения 9, в реальности цикл сработает 5 раз. Так как при достижении счетчиком i значения 5, сработает оператор break, и цикл завершится.

0

1

2

3

4

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

1 2 3 4 5 6 for (int i = 0; i < 9; i++) { if (i == 5) continue; Console.WriteLine(i); }

В этом случае цикл, когда дойдет до числа 5, которое не удовлетворяет условию проверки, просто пропустит это число и перейдет к следующей итерации:

0

1

2

3

4

6

7

8

 

Массивы

Многомерные массивы

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

1 2 3 int[] nums1 = new int[] { 0, 1, 2, 3, 4, 5 };   int[,] nums2 = { { 0, 1, 2 }, { 3, 4, 5 } };

Визуально оба массива можно представить следующим образом:

Одномерный массив nums1

0 1 2 3 4 5

Двухмерный массив nums2

0 1 2
3 4 5

Поскольку массив nums2 двухмерный, он представляет собой простую таблицу. Объявление трехмерного массива могло бы выглядеть так:

1 int[,,] nums3 = new int[2, 3, 4];

Все возможные способы определения двухмерных массивов:

1 2 3 4 5 6 int[,] nums1; int[,] nums2 = new int[2, 3]; int[,] nums3 = new int[2, 3] { { 0, 1, 2 }, { 3, 4, 5 } }; int[,] nums4 = new int[,] { { 0, 1, 2 }, { 3, 4, 5 } }; int[,] nums5 = new [,]{ { 0, 1, 2 }, { 3, 4, 5 } }; int[,] nums6 = { { 0, 1, 2 }, { 3, 4, 5 } };

Определенную сложность может представлять перебор многомерного массива. Прежде всего надо учитывать, что длина такого массива - это совокупное количество элементов.

1 2 3 4 int[,] mas = { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 }, { 10, 11, 12 } }; foreach (int i in mas) Console.Write($"{i} "); Console.WriteLine();

В данном случае длина массива mas равна 12. И цикл foreach выводить все элементы массива в строку:

1 2 3 4 5 6 7 8 9 10 11 12

Но что если мы хотим отдельно пробежаться по каждой строке в таблице? В этом случае надо получить количество элементов в размерности. В частности, у каждого массива есть метод GetUpperBound(dimension), который возвращает индекс последнего элемента в определенной размерности. И если мы говорим непосредственно о двухмерном массиве, то первая размерность (с индексом 0) по сути это и есть таблица. И с помощью выражения mas.GetUpperBound(0) + 1 можно получить количество строк таблицы, представленной двухмерным массивом. А через mas.Length / rows можно получить количество элементов в каждой строке:

1 2 3 4 5 6 7 8 9 10 11 12 13 int[,] mas = { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 }, { 10, 11, 12 } };   int rows = mas.GetUpperBound(0) + 1; int columns = mas.Length / rows;   for (int i = 0; i < rows; i++) { for (int j = 0; j < columns; j++) { Console.Write($"{mas[i, j]} \t"); } Console.WriteLine(); }
1        2        34        5        67        8        910      11      12

Массив массивов

От многомерных массивов надо отличать массив массивов или так называемый "зубчатый массив":

1 2 3 4 int[][] nums = new int[3][]; nums[0] = new int[2] { 1, 2 }; // выделяем память для первого подмассива nums[1] = new int[3] { 1, 2, 3 }; // выделяем память для второго подмассива nums[2] = new int[5] { 1, 2, 3, 4, 5 }; // выделяем память для третьего подмассива

Здесь две группы квадратных скобок указывают, что это массив массивов, то есть такой массив, который в свою очередь содержит в себе другие массивы. В данном случае у нас массив nums содержит три массива. Причем размерность каждого из этих массивов может не совпадать.

Зубчатый массив nums

1 2
1 2 3
1 2 3 4 5

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

1 2 3 4 5 6 int[][,] nums = new int[3][,] { new int[,] { {1,2}, {3,4} }, new int[,] { {1,2}, {3,6} }, new int[,] { {1,2}, {3,5}, {8, 13} } };

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

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

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 int[][] numbers = new int[3][]; numbers[0] = new int[] { 1, 2 }; numbers[1] = new int[] { 1, 2, 3 }; numbers[2] = new int[] { 1, 2, 3, 4, 5 }; foreach(int[] row in numbers) { foreach(int number in row) { Console.Write($"{number} \t"); } Console.WriteLine(); }   // перебор с помощью цикла for for (int i = 0; i<numbers.Length;i++) { for (int j =0; j<numbers[i].Length; j++) { Console.Write($"{numbers[i][j]} \t"); } Console.WriteLine(); }

Задачи с массивами

Рассмотрим пару задач для работы с массивами.

Найдем количество положительных чисел в массиве:

1 2 3 4 5 6 7 8 9 10 int[] numbers = { -4, -3, -2, -1, 0, 1, 2, 3, 4 }; int result = 0; foreach(int number in numbers) { if(number > 0) { result++; } } Console.WriteLine($"Число элементов больше нуля: {result}");

Вторая задача - инверсия массива, то есть переворот его в обратном порядке:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 int[] numbers = { -4, -3, -2, -1,0, 1, 2, 3, 4 };   int n = numbers.Length; // длина массива int k = n / 2; // середина массива int temp; // вспомогательный элемент для обмена значениями for(int i=0; i < k; i++) { temp = numbers[i]; numbers[i] = numbers[n - i - 1]; numbers[n - i - 1] = temp; } foreach(int i in numbers) { Console.Write($"{i} \t"); }

Поскольку нам надо изменять элементы массива, то для этого используется цикл for. Алгоритм решения задачи подразумевает перебор элементов до середины массива, которая в программе представлена переменной k, и обмен значений элемента, который имеет индекс i, и элемента с индексом n-i-1.

Программа сортировки массива

Методы

Функции

В отличие от процедур функции возвращают определенное значение. Например, определим пару функций:

1 2 3 4 5 6 7 8 9 int Factorial() { return 1; }   string Hello() { return "Hell to World"; }

В функции в качестве типа возвращаемого значения вместо void используется любой другой тип. В данном случае тип int. Функции также отличаются тем, что мы обязательно должны использовать оператор return, после которого ставится возвращаемое значение.

Также стоит отметить, что возвращаемое значение всегда должно иметь тот же тип, что значится в определении функции. То есть если у нас функция возвращает значение типа int, поэтому после оператора return стоит число 1 - которое неявно является объектом типа int.

Модификатор out

Выше мы использовали входные параметры. Но параметры могут быть также выходными. Чтобы сделать параметр выходным, перед ним ставится модификатор out:

1 2 3 4 static void Sum(int x, int y, out int a) { a = x + y; }

Здесь результат возвращается не через оператор return, а через выходной параметр. Использование в программе:

1 2 3 4 5 6 7 8 9 10 11 12 static void Main(string[] args) { int x = 10;   int z;   Sum(x, 15, out z);   Console.WriteLine(z);   Console.ReadLine(); }

Причем, как и в случае с ref ключевое слово out используется как при определении метода, так и при его вызове.

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

1 2 3 4 static void Sum(int x, int y, out int a) { Console.WriteLine(x+y); }

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

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 static void Main(string[] args) { int x = 10; int area; int perimetr; GetData(x, 15, out area, out perimetr); Console.WriteLine("Площадь : " + area); Console.WriteLine("Периметр : " + perimetr);   Console.ReadLine(); } static void GetData(int x, int y, out int area, out int perim) { area= x * y; perim= (x + y)*2; }

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

По сути, как и в случае с ключевым словом ref, ключевое слово out применяется для для передачи аргументов по ссылке. Однако в отличие от ref для переменных, которые передаются с ключевым словам out, не требуется инициализация. И кроме того, вызываемый метод должен обязательно присвоить им значение.

Стоит отметить, что в версии C# 7.0 (Visual Studio 2017) можно определять переменные в непосредственно при вызове метода. То есть вместо:

1 2 3 4 5 6 int x = 10; int area; int perimetr; GetData(x, 15, out area, out perimetr); Console.WriteLine("Площадь : " + area); Console.WriteLine("Периметр : " + perimetr);

Мы можем написать:

1 2 3 4 int x = 10; GetData(x, 15, out int area, out int perimetr); Console.WriteLine("Площадь : " + area); Console.WriteLine("Периметр : " + perimetr);

Необязательные параметры

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

1 2 3 4 static int OptionalParam(int x, int y, int z=5, int s=4) { return x + y + z + s; }

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

1 2 3 4 5 6 7 8 static void Main(string[] args) { OptionalParam(2, 3);   OptionalParam(2,3,10);   Console.ReadLine(); }

Именованные параметры

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

1 2 3 4 5 6 7 8 9 10 11 12 13 static int OptionalParam(int x, int y, int z=5, int s=4) { return x + y + z + s; } static void Main(string[] args) { OptionalParam(x:2, y:3);   //Необязательный параметр z использует значение по умолчанию OptionalParam(y:2,x:3,s:10);   Console.ReadLine(); }

 

Перечисления enum

Конструкторы в структурах

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

1 Book book1;

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

1 2 3 book1.name = "Война и мир"; book1.author = "Л. Н. Толстой"; book1.year = 1869;

Вызов же конструктора по умолчанию позволяет автоматически проинициализировать поля структуры значениями по умолчанию (например, для числовых данных - это число 0):

1 Book book2=new Book(); // использование конструктора

Вызов конструктора имеет следующий синтаксис: new название_структуры ([список_параметров]). Теперь попробуем определить свой конструктор:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 struct Book { public string name; public string author; public int year;   // конструкор public Book(string n, string a, int y) { name = n; author = a; year = y; } public void Info() { Console.WriteLine($"Книга '{name}' (автор {author}) была издана в {year} году"); } }

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

1 2 Book book=new Book("Война и мир", "Л. Н. Толстой", 1869); book.Info();

Теперь нам не надо вручную присваивать значения полям структуры - их инициализацию выполнил конструктор.

Кортежи

Использование кортежей

Кортежи могут передаваться в качестве параметров в метод, могут быть возвращаемым результатом функции, либо использоваться иным образом.

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

1 2 3 4 5 6 7 8 9 10 11 12 13 static void Main(string[] args) { var tuple = GetValues(); Console.WriteLine(tuple.Item1); // 1 Console.WriteLine(tuple.Item2); // 3   Console.Read(); } private static (int, int) GetValues() { var result = (1, 3); return result; }

Здесь определен метод GetValues(), который возвращает кортеж. Кортеж определяется как набор значений, помещенных в круглые скобки. И в данном случае мы возвращаем кортеж из двух элементов типа int, то есть два числа.

Другой пример:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 static void Main(string[] args) { var tuple = GetNamedValues(new int[]{ 1,2,3,4,5,6,7}); Console.WriteLine(tuple.count); Console.WriteLine(tuple.sum);   Console.Read(); } private static (int sum, int count) GetNamedValues(int[] numbers) { var result = (sum:0, count: 0); for (int i=0; i < numbers.Length; i++) { result.sum += numbers[i]; result.count++; } return result; }

И также кортеж может передаваться в качестве параметра в метод:

1 2 3 4 5 6 7 8 9 10 11 12 13 static void Main(string[] args) { var (name, age) = GetTuple(("Tom", 23), 12); Console.WriteLine(name); // Tom Console.WriteLine(age); // 35 Console.Read(); }   private static (string name, int age) GetTuple((string n, int a) tuple, int x) { var result = (name: tuple.n, age: tuple.a + x); return result; }

 

Обработка исключений

Оператор throw

Чтобы сообщить о выполнении исключительных ситуаций в программе, можно использовать оператор throw. То есть с помощью этого оператора мы сами можем создать исключение и вызвать его в процессе выполнения. Например, в нашей программе происходит ввод строки, и мы хотим, чтобы, если длина строки будет больше 6 символов, возникало исключение:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 static void Main(string[] args) { try { string message = Console.ReadLine(); if (message.Length > 6) { throw new Exception("Длина строки больше 6 символов"); } } catch (Exception e) { Console.WriteLine("Ошибка: " + e.Message); } Console.ReadLine(); }

Фильтры исключений

В C# 6.0 (Visual Studio 2015) была добавлена такая функциональность, как фильтры исключений. Они позволяют обрабатывать исключения в зависимости от определенных условий:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 int x = 1; int y = 0;   try { int result = x / y; } catch(Exception ex) when (y==0) { Console.WriteLine("y не должен быть равен 0"); } catch(Exception ex) { Console.WriteLine(ex.Message); }

В данном случае будет выброшено исключение, так как y=0. Здесь два блока catch, но поскольку для первого блока указано условие с помощью ключевого слова when, то сработает первый блок catch. Если бы y не было бы равно 0, то сработал бы второй блок catch.

Переменные-ссылки и возвращение ссылки

Локальная переменная-ссылка

Для определения локальной переменной-ссылки (ref local) перед ее типом ставится ключевое слово ref:

1 2 int x = 5; ref int xRef = ref x;

Здесь переменная xRef указывает не просто на значение переменной x, а на область в памяти, где располагается эта переменная. Для этого перед x также указывается ref.

При этом мы не можем просто определить переменную-ссылку, нам обязательно надо присвоить ей некоторое значение. Так, следующий код вызовет ошибку:

1 ref int xRef; // ошибка

Получив ссылку, мы можем манипулировать значением по этой ссылке. Например:

1 2 3 4 5 6 7 8 9 10 static void Main(string[] args) { int x = 5; ref int xRef = ref x; Console.WriteLine(x); // 5 xRef = 125; Console.WriteLine(x); // 125   Console.Read(); }

Классы и объекты

Конструкторы

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

Если в классе не определено ни одного конструктора, то для этого класса автоматически создается конструктор без параметров.

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

1 2 3 4 5 6 7 8 9 10 11 12 13 14 class Program { static void Main(string[] args) { Person tom = new Person(); tom.GetInfo(); // Имя: Возраст: 0   tom.name = "Tom"; tom.age = 34; tom.GetInfo(); // Имя: Tom Возраст: 34   Console.Read(); } }

Для создания объекта Person используется выражение new Person(). Оператор newвыделяет память для объекта Person. И затем вызывается конструктор по умолчанию, который не принимает никаких параметров. В итоге после выполнения данного выражения в памяти будет выделен участок, где будут храниться все данные объекта Person. А переменная tom получит ссылку на созданный объект.

Если конструктор не инициализирует значения переменных объекта, то они получают значения по умолчанию. Для переменных числовых типов это число 0, а для типа string и классов - это значение null (то есть фактически отсутствие значения).

После создания объекта мы можем обратиться к переменным объекта Person через переменную tom и установить или получить их значения, например, tom.name = "Tom";.

Консольный вывод данной программы:

Имя:   Возраст: 0Имя: Tom          Возраст: 34

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

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 class Person { public string name; public int age;   public Person() { name = "Неизвестно"; age = 18; } public Person(string n) { name = n; age = 18; } public Person(string n, int a) { name = n; age = a; } public void GetInfo() { Console.WriteLine($"Имя: {name} Возраст: {age}"); } }

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

1 2 3 4 5 6 7 8 9 10 11 12 13 static void Main(string[] args) { Person tom = new Person(); // вызов первого конструктора без параметров tom.GetInfo(); // Имя: Неизвестно Возраст: 18   Person bob = new Person("Bob"); //вызов второго конструктора с одним параметром bob.GetInfo(); // Имя: Bob Возраст: 18   Person sam = new Person("Sam", 25); // вызов третьего конструктора с двумя параметрами sam.GetInfo(); // Имя: Sam Возраст: 25   Console.Read(); }

Консольный вывод данной программы:

Имя: Неизвестно Возраст: 18Имя: Bob Возраст: 18Имя: Sam Возраст: 25

При этом если в классе определены конструкторы, то при создании объекта необходимо использовать один из этих конструкторов.

Ключевое слово this

Ключевое слово this представляет ссылку на текущий экземпляр класса. В каких ситуациях оно нам может пригодиться? В примере выше определены три конструктора. Все три конструктора выполняют однотипные действия - устанавливают значения полей name и age. Но этих повторяющихся действий могло быть больше. И мы можем не дублировать функциональность конструкторов, а просто обращаться из одного конструктора к другому через ключевое слово this, передавая нужные значения для параметров:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class Person { public string name; public int age;   public Person() : this("Неизвестно") { } public Person(string name) : this(name, 18) { } public Person(string name, int age) { this.name = name; this.age = age; } public void GetInfo() { Console.WriteLine($"Имя: {name} Возраст: {age}"); } }

В данном случае первый конструктор вызывает второй, а второй конструктор вызывает третий. По количеству и типу параметров компилятор узнает, какой именно конструктор вызывается. Например, во втором конструкторе:

1 2 3 public Person(string name) : this(name, 18) { }

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

Также стоит отметить, что в третьем конструкторе параметры называются также, как и поля класса.

1 2 3 4 5 public Person(string name, int age) { this.name = name; this.age = age; }

И чтобы разграничить параметры и поля класса, к полям класса обращение идет через ключевое слово this. Так, в выражении this.name = name; первая часть this.name означает, что name - это поле текущего класса, а не название параметра name. Если бы у нас параметры и поля назывались по-разному, то использовать слово this было бы необязательно. Также через ключевое слово this можно обращаться к любому полю или методу.

Инициализаторы объектов

Для инициализации объектов классов можно применять инициализаторы. Инициализаторы представляют передачу в фигурных скобках значений доступным полям и свойствам объекта:

1 2 Person tom = new Person { name = "Tom", age=31 }; tom.GetInfo(); // Имя: Tom Возраст: 31

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

 

                                            

 

Язык C# и платформа .NET

Последнее обновление: 09.03.2017

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

По сравнению с другими языками C# достаточно молодой, но в то же время он уже прошел большой путь. Первая версия языка вышла вместе с релизом Microsoft Visual Studio .NET в феврале 2002 года. Текущей версией языка является версия C# 7.0, которая вышла в 7 марта 2017 года вместе с Visual Studio 2017.

C# является языком с Си-подобным синтаксисом и близок в этом отношении к C++ и Java. Поэтому, если вы знакомы с одним из этих языков, то овладеть C# будет легче.

C# является объектно-ориентированным и в этом плане много перенял у Java и С++. Например, C# поддерживает полиморфизм, наследование, перегрузку операторов, статическую типизацию. Объектно-ориентированный подход позволяет решить задачи по построению крупных, но в тоже время гибких, масштабируемых и расширяемых приложений. И C# продолжает активно развиваться, и с каждой новой версией появляется все больше интересных функциональностей, как, например, лямбды, динамическое связывание, асинхронные методы и т.д.

Роль платформы .NET

Когда говорят C#, нередко имеют в виду технологии платформы .NET (WPF, ASP.NET). И, наоборот, когда говорят .NET, нередко имеют в виду C#. Однако, хотя эти понятия связаны, отождествлять их неверно. Язык C# был создан специально для работы с фреймворком .NET, однако само понятие .NET несколько шире.

Как-то Билл Гейтс сказал, что платформа .NET - это лучшее, что создала компания Microsoft. Возможно, он был прав. Фреймворк .NET представляет мощную платформу для создания приложений. Можно выделить следующие ее основные черты:

  • Поддержка нескольких языков. Основой платформы является общеязыковая среда исполнения Common Language Runtime (CLR), благодаря чему .NET поддерживает несколько языков: наряду с C# это также VB.NET, C++, F#, а также различные диалекты других языков, привязанные к .NET, например, Delphi.NET. При компиляции код на любом из этих языков компилируется в сборку на общем языке CIL (Common Intermediate Language) - своего рода ассемблер платформы .NET. Поэтому мы можем сделать отдельные модули одного приложения на отдельных языках.
  • Кроссплатформенность. .NET является переносимой платформой (с некоторыми ограничениями). Например, последняя версия платформы на данный момент .NET Framework поддерживается на большинстве современных ОС Windows (Windows 10/8.1/8/7/Vista). А благодаря проекту Mono можно создавать приложения, которые будут работать и на других ОС семейства Linux, в том числе на мобильных платформах Android и iOS.
  • Мощная библиотека классов. .NET представляет единую для всех поддерживаемых языков библиотеку классов. И какое бы приложение мы не собирались писать на C# - текстовый редактор, чат или сложный веб-сайт - так или иначе мы задействуем библиотеку классов .NET.
  • Разнообразие технологий. Общеязыковая среда исполнения CLR и базовая библиотека классов являются основой для целого стека технологий, которые разработчики могут задействовать при построении тех или иных приложений. Например, для работы с базами данных в этом стеке технологий предназначена технология ADO.NET. Для построения графических приложений с богатым насыщенным интерфейсом - технология WPF. Для создания веб-сайтов - ASP.NET и т.д.

Также еще следует отметить такую особенность языка C# и фреймворка .NET, как автоматическая сборка мусора. А это значит, что нам в большинстве случаев не придется, в отличие от С++, заботиться об освобождении памяти. Вышеупомянутая общеязыковая среда CLR сама вызовет сборщик мусора и очистит память.


Поделиться:



Последнее изменение этой страницы: 2019-04-19; Просмотров: 487; Нарушение авторского права страницы


lektsia.com 2007 - 2024 год. Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав! (0.236 с.)
Главная | Случайная страница | Обратная связь