22 KiB
C#
Project file
Project file - файл для конфигурации проекта в формате xml
Поля:
- OutputType (Exe | Dll) - во что компилировать
- TargetFramework (net7.0) - версия .NET
Переменные
string name = "Tom";
string name2;
name2 = "Bob";
Константы
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, то используются суффиксы
float a = 3.14f;
decimal b = 3.14m;
Для целочисленных по умолчанию int
int a = 5;
uint b = 5u;
long c = 5l;
ulong d = 5ul;
Неявная типизация
var hello = "Hello world";
Для var объявление и инициализация должны идти вместе
// Так низя: ошибка
var c;
c = 5;
Вывод в терминал
Console.WriteLine("Привет");
Console.Write("Привет ");
Console.Write("Мир");
Выведет:
Привет
Привет мир
Форматированный вывод
string name = "John";
string age = 34;
Console.WriteLine($"{name} is {age} years old")
Ввод данных
string? name = Console.ReadLine();
int foo = Convert.ToInt32(Console.ReadLine());
Простые операторы
- +
- -
- *
- /
- %
Преобразование базовых типов данных
Операция сложения и вычитания возвращает int, если типы, на которые применяется операция, <= int (byte, short, int)
Неявное преобразование
Работает только на расширение
byte a = 4;
ushort = a;
Явное преобразовывание
Работает на расширение и на сужение
byte a = 5;
byte b = 4;
byte c = (byte) (a+b);
Ветвление
int a = 5;
if (a == 5) {
Console.WriteLine("It works");
} else {
Console.WriteLine("Cosmic ray!");
}
Тернарный оператор
Краткая возвращающая форма if
int x = 3;
int y = 5;
string z = x < y ? "Works" : "Cosmic ray";
Switch
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
Массивы
Массивы позволяют хранить заранее известное кол-во элементов вместе
Инициализация массивов
int[] a = new int[3] {10, 20, 30};
double[] b = new double[5];
float[] c = {1.5f, 2.3f}; // Поймёт, что нужна длина 2
Длинна массивов
int[] a = {10, 20};
int len = a.Length;
Методы
int[] a = {10, 30, 20};
Array.IndexOf(a, 10) // Вернёт 0. -1, если не найдёт.
Array.Sort(a); // Сортирует по возрастанию. Станет {10, 20, 30}
Двумерный массив
int[,] a = new int[10,10];
Динамический массив
using System.Collections.Generic;
List<string> words = new List<string> ();
words.Add("Foo");
words.Add("Bar");
words.RemoveAt(1);
Random
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
Функции
void Foo() {
Console.WriteLine("Hello");
}
// Краткая форма
void Bar() => Console.WriteLine("Hi");
Параметры функции
int Sum(int x, int y) => x + y; // Тут x и y - параметры
Sum(5, 6); // Тут 5 и 6 - аргументы
Необязательные параметры
void PrintPerson(string name, int age = 1) {
Console.WriteLine($"{name} - {age}");
}
PrintPerson("John", 5);
PrintPerson(age: 5, name: "Bob"); // Именованные параметры
Передача по ссылке
При передаче по ссылке и зменения внутри функции меняют оригинальное значение
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
void Sum(int a, int b, out int result) {
result = a + b;
}
int result;
Sum(5, 6, out result);
Входные параментры
Передача по неизменяемой ссылке. Судя по всему полезно только для массивов
int Sum(in int a, in int b) {
return a + b;
}
int a = 5;
int b = 6;
Sum(a, b);
params
params позволяет передать не определённое кол-во параметров. После него не может быть больше параметров
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();
Возвращение значений
int Sum1(int a, int b) {
return a + b;
}
int Sum2(int a, int b) => a + b;
Рекурсия
int Factorial(int n) {
if (n == 0) return 1;
return n * Factorial(n - 1);
}
Функции внути функций
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 используется для хранения состояния
enum Operation {
Add,
Sub
}
Operation foo = Operation.Add;
Тип констант перечисления
enum Time: byte {
Morning,
Afternoon,
Evening,
Night
}
Тип обязательно должен быть целочисленным. По умолчанию int
Задание значения для Enum
enum DayTime {
Morning = 3,
Afternoon // 4
}
OOP
OOP - зло, C# - OOP -> 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");
}
}
}
Конструкторов может быть несколько
Инициализаторы
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";
}
Деконструкторы
Деконструкторы - методы, чтобы "разобрать" объект на составные части
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
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
- Классы
- Интерфейсы
- Делегаты
Пространства имён
Пространства имён позволяют организовыывать код программы в логические блоки
namespace Foo {
namespace Bar {
class Baz {}
}
}
Пространство имён подключается с помощью using
using Foo;
using Foo.Bar; // Для этого снача using Foo писать не надо
global using Foo.Bar; // Подключает этот namespace везде. Зачастую такое в файле GlobalUsings.cs
Модификаторы доступа
- private - только в рамках класса/структуры (по умолчанию для полей)
- private protected - в рамках класса и наследников внутри сборки
- file - только в текущем файле
- protected - внутри класса и наследниках, которые могут находиться в других сборках
- internal - доступен только внутри сборки (по умолчанию для классов)
- protected internal - доступен из любого места в сборке и классах наследниках, которые могут быть в других сборках
- public - достпупен везде
Свойства
class Person {
string name = "Undefined";
public string Name {
get {
return name;
}
set {
name = value;
}
}
}
Можно и так
class Person
{
public string Name { get; set; }
public int Age { get; set; } = 69;
public Person(string name) {
this.Name = name;
}
}
Модификатор required
Данный модификатор делает поле или свойство обязательным для инициализации
Person tom = new Person(); // Ошибка
Person bob = new Person { Name = "Bob" }; // Вот так хорошо
class Person {
public required string Name { get; set; }
}
Перегрузка методов
Перегрузка методов - возможность создать несколько методов, принимающих разное кол-во параметров или разные типы параметров, с одним и тем же именем. Выходной тип не может отличаться.
int Sum(int a, int b) {
return a + b;
}
int Sum(int a, int b, int c) {
return a + b + c;
}
static
Статические поля, методы, свойства относятся ко всему классу. Обращение к ним идёт через сам класс. Статичные поля инициализируются во время запуска программы и находятся в специальной области памяти для static'ов. Статические классы могут содержать только статичные поля/свойста/методы.
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, значение можно присваивать только при объявлении или конструкторе
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
}
Наследование
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 - явное преобразование типа к производному
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 используется в дочернем
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
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. Абстрактные методы не имеют тела. Все не абстрактные наследники должны определять все абстрактные методы и свойствая
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)
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;
}
Оценка сложности
В программировании, вычислительную скорость алгоритмов обычно оценивают по количеству действий, который выполняет алгоритм, и по количеству используемой памяти
Виды:
- O(1) - constant time
- O(n) - линейная
- O(log n) - логарифмическая
- O(n**2) - квадратичная
Обработка ошибок
try {
// Тут злой код (он кидается)
} catch {
// Тут мы обрабатываем летящие в нас камни
} finally {
// This is fine... Anyway
}
Иногда может использоваться bool
int Square(string data)
{
if (int.TryParse(data, out var x))
{
return x * x;
}
else
{
return default(int);
}
}
Можно обрабатывать конкретные ошибки
int x = 1;
int y = 0;
try
{
int result = x / y;
}
catch (DivideByZeroException) when (y == 0 && x == 0)
{
Console.WriteLine("Оба ноль");
}
catch (DivideByZeroException ex)
{
Console.WriteLine(ex.Message);
}
Класс Exception
Класс Exception - базовый класс всех ошибок
Поля:
- InnerException
- Message
- Source - имя объекта/сборки, которое вызвало исключение
- StackTrace
- TargetSote - метод, в котором случилось исключение
Throw
try {
throw new Exception("СТРАШНА ВЫРУБАЙ");
} catch {
Console.WriteLine("This is fine");
}
Делегаты
Делегаты - указатели на методы. Методы должны полностью соответсвовать сигнатуре делегата. Сигнатура - возвращаемый тип и передоваемые параметры
Используются для:
- Callbacks
- Events - в языке есть хитрый механизм на это дело
Message? mes = Hello;
mes();
mes = new Hello().Display;
mes();
mes = new Message(Hello); // Можно и так присвоить
mes += new Hello().Display;
mes(); // Вызывается и Hello, и Display
mes += Hello; // Hello теперь будет вызываться 2 раза
mes -= Hello; // Удалит последний Hello из списка вызова
if (mes == null)
{
return;
}
mes += mes; // Делегаты можно складывать
mes.Invoke(); // Тоже самое, что и просто вызвать делегат
void Hello()
{
Console.WriteLine("Hello World");
}
delegate void Message();
class Hello
{
public void Display()
{
Console.WriteLine("Hello there");
}
}
Если в делегате несколько методов, то вернётся последнее значение.
Делегаты могут быть параметрами методов и возвращаемым типом.
Обобщённые делегаты
delegate T Operation<T, K>(K val);