Основы C#
October 7, 2022

Первая программа

Привет! В этой статье разберем правила построения программы на C#, структуру проекта, немного поговорим о переменных и основных операциях с ними, а также соберем и запустим первую программу. Летс го)

Понятие программы

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

Дональд Кнут в первом тому своем труда "Искусство программирования" ооооочень подробно описал историю происхождения термина алгоритма и дал хорошее определение:

Алгоритм — это конечный набор правил, который определяет последовательность операций для решения конкретного множества задач и обладает пятью важными чертами: конечность, определённость, ввод, вывод, эффективность

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

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

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

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

Базовые принципы построение программы на языке C#

Код каждой программы на языке C# должен отвечать 4-м основным принципам для его успешной компиляции и выполнения:

  1. В С# каждая команда начинается с новой строки и заканчивается точкой с запятой;
  2. Команда не может существовать сама по себе. Совокупность команд и различных управляющих конструкций, реализующих определенную логику или функциональную возможность, называется методом.
  3. Программы в С# состоят из классов. Класс - это некоторый шаблон, описывающий некоторую группу объектов(сущностей) с помощью набора переменных (данных) и методов для работы с ними. Каждый класс следует описывать в отдельном файле, имя которого следует называть именем класса.
  4. Выполнение программы начинается с метода Main. Минимальная исполняемая программа в С# всегда имеет метод Main.

Первая программа

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

Сначала хочу показать, как это все делается, используя минимальные средства, далее установим и настроим IDE. Но перед началом необходимо установить .NET с сайта Microsoft для своей операционки, ничего сложного там нет качаете инсталятор и устанавлаиваете, тыкая "далее", либо с помощью средств командной строки, там подробно все расписано.

Итак, для начала создаем отдельную папочку для проекта, в моем примере это FirstProgram, открываем блокнот и пишем простейший класс Robot с одним методом Main, сохраняем файл класса с расширением .cs.

А вы думали, я вам код текстом дам?! Нет, переписывайте, набивайте руку)))

Для сборки проекта .NET также необходим файл .csproj с неким "описанием проекта", как он будет собираться. Создаем его FirstProgram.csproj)))

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net6.0</TargetFramework>
  </PropertyGroup>
</Project>

В нем сейчас указан выходной тип сборки, исполняемый файл exe(т.к. у меня шиндовс), и версия .NET 6.

Теперь открываем открываем папочку с нашим проектом и прописываем команду dotnet build для сборки проекта, с помощью драйвера .NET.

Для запуска программы можно перейти в директорию bin, в которой будет располагаться полученный исполняемый файл, либо воспользоваться командой dotnet run

Компоненты простейшей программы

Теперь поговорим о том, что мы вообще написали.

Пространство имен и директива using

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

В качестве примера можем взять проект из двух файлов Robot1.cs и Robot2.cs, при чем оба класса мы "нечаянно" назвали Robot, а в методе Main мы обращаемся к методу Say() нашего робота. При компоновке возникнет, соответственно неоднозначность. Компилятор не сможет понять, какой именно класс мы хотим использовать.

Классы, которые не помещенны в какое-то собственное пространство имен, лежат в глобальном пространстве имен

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

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

namespace Имя
{ 
    Определение типов 
}

Хорошим тоном для выбора пространства имен считается: Компания.Проект.ИмяПрограммы

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

ИмяПространстваИмен.ИмяКласса.Метод

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

  • Мы находимся в том же самом пространстве имен. То есть, если класс объявлен внутри фигурных скобок пространства имен и используется в них же;
  • Пространство имен подключено с помощью директивы using.

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

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

Переменные и их представление в памяти

Любая программа работает с данными, которые могут вводиться пользователем, вычисляться, подтягиваться из других ресурсов или прописываться в коде программы, эти данные надо где-то хранить. Постоянные данные хранятся на жестком диске, а временные данные — в оперативной памяти. Под временными данными я подразумеваю все, что необходимо программе для расчетов. Для того чтобы обработать данные, их необходимо сначала подгрузить в оперативную память и затем передать процессору, так как процессор не умеет самостоятельно хранить большое количество информации и не имеет прямого доступа к оперативной памяти.

Так вот, память можно себе представить как некоторую сетку с множеством ячеек с уникальными номерами, каждая из которых хранит (в основном) по 1 байту (8 бит) информации. После загрузки некоторых данных (например целого числа) в оперативную память, эти данные будут располагаться там последовательно, начиная с ячейки с неким адресом n, а заканчивая ячейкой n + size, где size - размер данных.

Мы можем обращаться по адресу памяти, читать от туда данные и менять их, но, согласитесь, человеку было бы не удобно работать с такими адресами напрямую 0x7ffda41c5fac)) Для этого были придуманы переменные и константы.

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

тип имяПеременной;

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

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

Но при именовании переменной следует отражать ее назначение и по правилам кодстайла именовать, начиная с маленькой буквы (например так arraySize)

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

int a = 5;
Console.WriteLine(a); //5
a = 4;
Console.WriteLine(a); //4

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

const int someSize = 10;
Console.WriteLine(someSize);

Типы данных

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

Тип данных однозначно определяет:

  • внутреннее представление данных, а следовательно, и множество их возможных значений;
  • допустимые действия над данными (операции и методы).

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

Целочисленные типы

Числа с плавающей точкой

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

  • Сложение +
int a = 10;
int b = a + 5; //15
double c = 10 + 15.156; //25.156
  • Вычитание -
int a = 15;
int b = a - 5; //10
  • Умножение *
int a = 15;
int b = a * 5; //75
  • Деление /
int a = 5 / 2; //2

double b = 5 / 2; //2

doubcle c = 5.0 / 2; //2.5

double d = 10;
double e = 3;
double g = d / e; //3.333333

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

  • Остаток от деления %
double d = 10;
double e = 3;
double g = d % e; //1
  • Унарный минус
int a = 1;
a = -a; //-1
  • Операции инкремента и декремента

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

int a = 5;
a++; // a=6
int b = a++; // a=7 b=6

int c = 8;
int d = ++c; // c=9 d=9

int n = 16;
n--; // 15
int t = --n; // t=14 n=14
  • Составные операции присваивания

Помимо простой операции присваивания = в C# определены составные операции присваивания. Как и операция =, они все приводят к присваиванию значения той переменной, которая находится в их левой части, при выполнении с ней операции с выражением, находящимся в правой части.

int a = 5;
int b = 6;
a += b; // a=11
a -= b - 4 // a=1

Булевый тип

Данные тиа bool обычно используют для проверки тех или иных условий. Литералы булевского типа – это ключевые слова true и false.

int a = 5;
int b = 5;
int c = 10;

bool d = a == b; //true
d = a == c; // false
d = a < c; // true 

Важно отметить, что в языке C# не разрешено преобразование булевого типа в числовые значения, true в 1 и false в 0, как это реализуется в других языках, например в C++.

Символьный тип

Значение символьного типа может хранить одиночный символ либо числовой код в кодировке Unicode, заключенным в одинарные кавычки.
Unicode – это набор символов, включающий символы латинского алфавита, символы национальных алфавитов, цифры, знаки препинания и специальные символы. Каждый символ набора Unicode имеет код из диапазона от 0 до 65535. Первые 128 символов Unicode совпадают с символами стандарной части таблицы ASCII.

char a = 'a';
char b = '\u0420';

String

Тип string является структурой данных, хранящей набор символов типа char. Этот тип немного отличается от всех остальных, мы о нем еще успеем много поговорить)
Строки указываются в двойных кавычках и обладают рядом свойств. Для сложения, конкатенации, строк можно использовать оператор +.

string h = "Hallo";
string w = "World" + h; // "Hallo World"

В строках можно указывать управляющие последовательности, например \n \t .

string a = "Hallo\nWorld";
/*
*Hallo
*World
*/

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

string path = @"C:\Users\artem\notSeriousSeeSharp\FirstProgram"

string name = "\"cool\" story"

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

Если по простому, то это штука, которая позволяет удобно форматировать строки и подставлять внутрь переменные и вычисляемые значения, которые в рантайме будут вычисляться и подставляться в строку)

Для определения строкового литерала в качестве интерполированной строки используется символ $ в начале строки.

Тут мы подставляем переменные name и years в последнюю строкку

Установка IDE

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

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

Для написания кода на C# я использую Visual Studio от Microsoft и всем советую. Еще есть довольно популярная среда ReSharper от JetBrains, но лично мне не зашло и многие вещи в нем казались избыточными и отвлекающими, а подсказки от Intelsense в Visual Studio 2022 вполне сопоставимы с тем, что предлагает Rider в ReSharper. Поэтому покажу, как установить на Windows мою любимую визуалочку🥰 и создать первый проект.

Переходим на официальный сайт макрософт и качаем инсталятор с версией Community Edition.

После загрузки инсталятора откроется окно с огромным количеством разных модулей, вам сейчас для одного шарпа необходим минимум из платформы .NET. Все остальное можно будет при надобности через этот же самый инсталятор впоследствии доустановить. Во вкладке "рабочие нагрузки" выбираете "разработка классических приложений .NET", все нужные пакеты там будут, и этого будет достаточно, чтобы писать консольные приложения и формошлепить десктопные приложения.

Жмякаете по кнопке установить в нижнем правом углу и у вас начнется загрузка. После установки, при запуске Visual Studio откроется окно для выбора открытия проекта или его создания. Создаем!

Откроется окно для выбора типа проекта с возможностью отфильтровать ненужное. Выставляем С# и консольное приложение, останется два типа проекта. Помним, что .Net Framework ныне не поддерживается и оно тянется легасем, и там старая версия язык C# 8, что нам не нравится и поэтому выбираем модное .NET Core приложение.

Прописываем название проекта и каталог.

Выбираем версию .NET, которая будет использована в проекте.

После этого Visual Studio создаст и откроет для нас проект

Слева находится "обозреватель решений", в котором находится структура проекта, через который мы можем добавить новые его составляющие, клацнув пкм по имени проекта. Для запуска проекта можно воспользоваться комбинацией клавиш ctrl + f5

И у вас наверняка сразу возникает вопрос, а что за фигня, где все то, про что ты нам втирал, где там эти намспайсы, классы методы?! Вон же, просто вывод в консоль, все запускается и работает! Это что получается, ты нас обманывал?!

На самом деле, нет) Для .NET 5 и более ранних версий шаблон приложения должен обязательно выглядеть как показывал я, с классом и точкой входа, функцией Main. А начиная с версии .NET 6 с C# 10, оба варианта допустимы.

При использовании .NET 6 необходимо только написать код метода Main, а компилятор самостоятельно сгенерирует класс Program с методом Main и поместит все команды верхнего уровня из Program.cs в этот Main метод. То есть все, что было сказано про обязательное наличие точки входа, метода Main, остается в силе)

В нашем файле Program.cs мы по прежнему можем создавать другие методы, их компилятор поместит в сгенерированный класс Program.


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

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

Подписывайтесь на наш несерьезный тг-канал, ставьте ваши импортозамещенные славные классы)