This repository has been archived on 2024-08-23. You can view files and clone it, but cannot push or open issues or pull requests.
lessons/C#/C#.md
2023-11-12 09:00:57 +00:00

865 lines
19 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# C\#
## Project file
Project file - файл для конфигурации проекта в формате xml
Поля:
* OutputType (Exe | Dll) - во что компилировать
* TargetFramework (net7.0) - версия .NET
## Переменные
```C#
string name = "Tom";
string name2;
name2 = "Bob";
```
## Константы
```C#
const string NAME = "Tom";
```
Константы:
* Определяются только во время компиляции
* Неявно являются статичными
## Типы данных
* bool
* sbyte (signed byte)
* byte
* short - 16 байт
* ushort (unsigned)
* int - 32 байта
* uint
* long - 64 байта
* ulong
* char - 16 байт. Знак unicode
* float 32 байт
* double - 64 байта
* decimal - 128 бит
* string
* object - аля Python Object
Числа в формате 3.14 по умолчанию double. Если нужен float или decimal, то используются суффиксы
```C#
float a = 3.14f;
decimal b = 3.14m;
```
Для целочисленных по умолчанию int
```C#
int a = 5;
uint b = 5u;
long c = 5l;
ulong d = 5ul;
```
## Неявная типизация
```C#
var hello = "Hello world";
```
Для var объявление и инициализация должны идти вместе
```C#
// Так низя: ошибка
var c;
c = 5;
```
## Вывод в терминал
```C#
Console.WriteLine("Привет");
Console.Write("Привет ");
Console.Write("Мир");
```
Выведет:
```text
Привет
Привет мир
```
## Форматированный вывод
```C#
string name = "John";
string age = 34;
Console.WriteLine($"{name} is {age} years old")
```
## Ввод данных
```C#
string? name = Console.ReadLine();
int foo = Convert.ToInt32(Console.ReadLine());
```
## Простые операторы
* \+
* \-
* \*
* /
* %
## Преобразование базовых типов данных
Операция сложения и вычитания возвращает int, если типы, на которые применяется операция, <= int (byte, short, int)
### Неявное преобразование
Работает только на расширение
```C#
byte a = 4;
ushort = a;
```
### Явное преобразовывание
Работает на расширение и на сужение
```C#
byte a = 5;
byte b = 4;
byte c = (byte) (a+b);
```
## Ветвление
```C#
int a = 5;
if (a == 5) {
Console.WriteLine("It works");
} else {
Console.WriteLine("Cosmic ray!");
}
```
### Тернарный оператор
Краткая возвращающая форма if
```C#
int x = 3;
int y = 5;
string z = x < y ? "Works" : "Cosmic ray";
```
### Switch
```C#
int number = 1;
switch (number) {
case 1:
Console.WriteLine("case 1");
goto case 5;
case 3:
Console.WriteLine("case 3");
break;
case 5:
Console.WriteLine("case 5");
break;
default:
Console.WriteLine("case whatever");
break;
}
```
### Циклы
Всё как в С++
* while
* do-while
* for
* foreach
## Массивы
Массивы позволяют хранить заранее известное кол-во элементов вместе
### Инициализация массивов
```C#
int[] a = new int[3] {10, 20, 30};
double[] b = new double[5];
float[] c = {1.5f, 2.3f}; // Поймёт, что нужна длина 2
```
### Длинна массивов
```C#
int[] a = {10, 20};
int len = a.Length;
```
### Методы
```C#
int[] a = {10, 30, 20};
Array.IndexOf(a, 10) // Вернёт 0. -1, если не найдёт.
Array.Sort(a); // Сортирует по возрастанию. Станет {10, 20, 30}
```
### Двумерный массив
```C#
int[,] a = new int[10,10];
```
### Динамический массив
```C#
using System.Collections.Generic;
List<string> words = new List<string> ();
words.Add("Foo");
words.Add("Bar");
words.RemoveAt(1);
```
## Random
```C#
Random r = new Random();
int a = r.Next(); // От 0 до 2 миллиардов
int b = r.Next(5); // От 0 до 5 (не включительно)
int c = r.Next(3, 5); // От 3 до 5
```
## Функции
```C#
void Foo() {
Console.WriteLine("Hello");
}
// Краткая форма
void Bar() => Console.WriteLine("Hi");
```
### Параметры функции
```C#
int Sum(int x, int y) => x + y; // Тут x и y - параметры
Sum(5, 6); // Тут 5 и 6 - аргументы
```
#### Необязательные параметры
```C#
void PrintPerson(string name, int age = 1) {
Console.WriteLine($"{name} - {age}");
}
PrintPerson("John", 5);
PrintPerson(age: 5, name: "Bob"); // Именованные параметры
```
#### Передача по ссылке
При передаче по ссылке и зменения внутри функции меняют оригинальное значение
```C#
void Inc(ref int a) {
a++;
}
void BadInc(int a) {
a++;
}
int b = 5;
Inc(ref b); // b == 6
BadInc(b); // b осталась 6
Inc(b); // Так низя
Inc(5); // И так низя
Inc(ref 5); // И так тоже низя
```
#### Выходной параметр
Функция может иметь несколько выходных параметров, а так лучше return
```C#
void Sum(int a, int b, out int result) {
result = a + b;
}
int result;
Sum(5, 6, out result);
```
#### Входные параментры
Передача по неизменяемой ссылке. Судя по всему полезно только для массивов
```C#
int Sum(in int a, in int b) {
return a + b;
}
int a = 5;
int b = 6;
Sum(a, b);
```
#### params
*params* позволяет передать не определённое кол-во параметров. После него не может быть больше параметров
```C#
int Sum(params int[] nums) {
int sum = 0;
foreach (int i in nums) {
sum += i;
}
return sum;
}
int[] nums = {1, 2, 3, 4};
Sum(nums);
Sum(1, 2, 3);
Sum(20);
Sum();
```
### Возвращение значений
```C#
int Sum1(int a, int b) {
return a + b;
}
int Sum2(int a, int b) => a + b;
```
### Рекурсия
```C#
int Factorial(int n) {
if (n == 0) return 1;
return n * Factorial(n - 1);
}
```
### Функции внути функций
```C#
int Sum2Arr(int[] arr1, int[] arr2) {
int Sum(int[] arr) {
int result = 0;
foreach (int i in arr) {
result += i;
}
return result;
}
return Sum(arr1) + Sum(arr2);
}
```
## Enum
Enum используется для хранения состояния
```C#
enum Operation {
Add,
Sub
}
Operation foo = Operation.Add;
```
### Тип констант перечисления
```C#
enum Time: byte {
Morning,
Afternoon,
Evening,
Night
}
```
Тип обязательно должен быть целочисленным. По умолчанию int
### Задание значения для Enum
```C#
enum DayTime {
Morning = 3,
Afternoon // 4
}
```
## OOP
OOP - зло, C# - OOP -> C# - зло
```C#
namespace Foo {
internal class Enemy {
public int hp;
public int armor;
public int damage;
public Enemy(int hp, int armor, int damage) {
this.hp = hp;
this.armor = armor;
this.damage = damage;
}
public void Move() {
Console.WriteLine("I am moving");
}
public void Attack() {
Console.WriteLine("I am attacking");
}
}
}
```
Конструкторов может быть несколько
### Инициализаторы
```C#
Person tom = new Person {name = "Tom", company = { title = "Microsoft" }};
class Person {
public string name = "Undefined";
public Company company;
public Person() {
company = new Company();
}
}
class Company {
public string title = "Unknown";
}
```
### Деконструкторы
Деконструкторы - методы, чтобы "разобрать" объект на составные части
```C#
class Person {
public string name;
public string age;
public void Deconstruct(out string personName, out int PersonAge) {
personName = name;
personAge = age;
}
}
```
### Структуры
Структуру - аналог классов с особенностями:
* Храняться на стеке
* Нет наследования: нельзя использовать *abstract*, *virtual*, *protected*
* При присваивании создаются копия
* Имеется ключевое слово with
```C#
Person tom;
tom.name = "Tom";
tom.age = 1;
Person bob = tom with { name = "Bob" };
Person Alice = new Person { name = "Alice", age = 5 };
Person Jan = new Person("Jan", 10);
struct Person
{
public string name;
public int age;
public Person(string name, int age)
{
this.name = name;
this.age = age;
}
}
```
### Типы значений и ссылочные типы
Типы значений (значимые типы) хранятся на стеке. При присваивании значимых типов создаётся копия. К таким типам относятся:
* Целочисленные значения
* Числа с плавающей точкой
* *decimal*
* *bool*
* *char*
* Перечисления
* Структуры
Ссылочные типы в куче, а ссылка на них в стеке. При присваивании ссылочных типов копируется только ссылка. К ним относятся:
* *string*
* *object*
* Классы
* Интерфейсы
* Делегаты
## Пространства имён
Пространства имён позволяют организовыывать код программы в логические блоки
```C#
namespace Foo {
namespace Bar {
class Baz {}
}
}
```
Пространство имён подключается с помощью *using*
```C#
using Foo;
using Foo.Bar; // Для этого снача using Foo писать не надо
global using Foo.Bar; // Подключает этот namespace везде. Зачастую такое в файле GlobalUsings.cs
```
## Модификаторы доступа
* *private* - только в рамках класса/структуры (по умолчанию для полей)
* *private protected* - в рамках класса и наследников внутри сборки
* *file* - только в текущем файле
* *protected* - внутри класса и наследниках, которые могут находиться в других сборках
* *internal* - доступен только внутри сборки (по умолчанию для классов)
* *protected internal* - доступен из любого места в сборке и классах наследниках, которые могут быть в других сборках
* *public* - достпупен везде
## Свойства
```C#
class Person {
string name = "Undefined";
public string Name {
get {
return name;
}
set {
name = value;
}
}
}
```
Можно и так
```C#
class Person
{
public string Name { get; set; }
public int Age { get; set; } = 69;
public Person(string name) {
this.Name = name;
}
}
```
### Модификатор *required*
Данный модификатор делает поле или свойство обязательным для инициализации
```C#
Person tom = new Person(); // Ошибка
Person bob = new Person { Name = "Bob" }; // Вот так хорошо
class Person {
public required string Name { get; set; }
}
```
## Перегрузка методов
Перегрузка методов - возможность создать несколько методов, принимающих разное кол-во параметров или разные типы параметров, с одним и тем же именем. Выходной тип не может отличаться.
```C#
int Sum(int a, int b) {
return a + b;
}
int Sum(int a, int b, int c) {
return a + b + c;
}
```
## *static*
Статические поля, методы, свойства относятся ко всему классу. Обращение к ним идёт через сам класс.
Статичные поля инициализируются во время запуска программы и находятся в специальной области памяти для static'ов.
Статические классы могут содержать только статичные поля/свойста/методы.
```C#
static class Person {
public static int retirementAge = 65;
public static int RetirementAge {
get {
return retirementAge;
}
set {
retirementAge = value;
}
}
public static int Foo(int bar) {
return bar + 2;
}
}
```
## Модификатор *readonly*
Полям, которые были объявлены *readoly*, значение можно присваивать только при объявлении или конструкторе
```C#
class Person {
public readonly string name = "Undefined";
public Person(string name) {
this.name = name;
}
public changeName(string name) {
this.name = name; // Ошибка
}
}
readonly struct Person2 {
public readonly string Name { get; }; // Тут readonly не обязательно
public int Age { get; }; // Тоже как-бы readonly
}
```
## Наследование
```C#
Employee tom = new Employee("Tom", "Org");
tom.Name = "Tom";
tom.PrintName();
class Person {
private string _name = "";
public string Name {
get { return _name; }
set { _name = value }
}
public Person(string name) {
this.name = name;
}
public void PrintName() {
Console.WriteLine(_name)
}
}
sealed class Employee: Person { // sealed запрещает наследование от данного класса
public string company;
public Employee(string name, string company) : base(name) {
this.company = company;
base.PrintName(); // Обращение к функцианалу родителя
}
}
```
От статических классов тоже нельзя наследоваться.
## Преобразование типов
Все классы наследуются от Object. Upcasting - неявное преобразование к типу, который находится вверху иерархии классов. Downcasting - явное преобразование типа к производному
```C#
Employee tom = new Employee("Tom", "Org");
tom.Name = "Tom";
Person person = tom;
Employee employee = (Employee)person;
Employee? employee = person as Employee; // null если преобразовать не удалось
if (person is Employee employee) {
Console.WriteLine(employee.company);
} else {
Console.WriteLine("Преобразовать низя");
}
class Person {
private string _name = "";
public string Name {
get { return _name; }
set { _name = value }
}
public Person(string name) {
this.name = name;
}
public void PrintName() {
Console.WriteLine(_name)
}
}
sealed class Employee: Person {
public string company;
public Employee(string name, string company) : base(name) {
this.company = company;
}
}
```
## Переопределение
> Возможность изменить функцианальность метода родительского класса в дочернем. *virtual* помечает метод для переопределения в базовом классе, а *override* используется в дочернем
```C#
class Foo
{
public virtual void func()
{
Console.WriteLine("Foo");
}
public virtual int Baz
{
get => 5;
set { Console.WriteLine(value); }
}
}
class Bar : Foo
{
public override sealed void func() // sealed запрещает дальнейшее переопределение
{
Console.WriteLine("Bar");
}
public override int Baz
{
get => 6;
}
}
```
## Shadowing / Hiding
```C#
class Foo {
public const string class_ = "Foo";
public int Baz {
get => 5;
}
public void Print() {
Console.WriteLine("Foo");
}
}
class Bar : Foo {
public new const string class_ = "Bar";
public new int Baz {
get => 6;
}
public new void Print() {
Console.WriteLine("Bar");
}
}
```
## Абстрактные классы
> Для представления сущностей, не имеющих конкретного воплощения, предназначены абстрактные классы. От них нельзя создать объект. Абстрактный класс может содержать абстрактные методы и свойства, которые не могут быть private. Абстрактные методы не имеют тела. Все не абстрактные наследники должны определять все абстрактные методы и свойствая
```C#
abstract class Transport
{
public abstract void Move();
public abstract string Name
{
get;
set;
}
}
class Car : Transport
{
public override void Move()
{
Console.WriteLine("Car moves");
}
public override string Name { get; set; } = "Car";
}
```
## Обобщения (generics)
```C#
Person<int> fooBar = new Person<int>(10, "Foobar"); // или new(10, "Foobar")
int x = 5;
int y = 10;
Swap<int>(ref x, ref y) // или Swap(ref x, ref y)
class Person<T>
{
public T Id { get; }
public string Name { get; }
public Person(T id, string name)
{
Id = id;
Name = name;
}
}
void Swap<T>(ref T x, ref T y) {
T tmp = x;
x = y;
y = tmp;
}
```
## Оценка сложности
> В программировании, вычислительную скорость алгоритмов обычно оценивают по количеству действий, который выполняет алгоритм, и по количеству используемой памяти