C# 1001 notes
6.51K subscribers
329 photos
9 videos
2 files
313 links
Регулярные короткие заметки по C# и .NET.

Просто о сложном для каждого.

admin - @haarrp
加入频道
Встроенные типы и C# keywords

Язык C# включает в себя большой набор встроенных (примитивных) типов, которые мы можем использовать при разработке наших программ.

Каждый их них имеет своё ключевое слово (keyword), которое соответствует встроенному типу платформы .NET. Другими словами, keyword является лишь сокращением или синонимом (alias) для конкретного типа (реализация которого находится в библиотеке BCL - base class library).

Далее представлены все доступные нам встроенные типы в формате C# тип - .NET тип - границы значений:

🔸 bool – System.Boolean – 1 byte (true|false)

🔸 byte – System.Byte – 1 byte (0 to 255)

🔸 sbyte – System.SByte – 1 byte (-128 to 127)

🔸 short – System.Int16 – 2 bytes (-32,768 to 32,767)

🔸 ushort – System.UInt16 – 2 bytes (0 to 65,535)

🔸 int – System.Int32 – 4 bytes (-2,147,483,648 to 2,147,483,647)

🔸 uint – System.UInt32 – 4 bytes (0 to 4,294,967,295)

🔸 long – System.Int64 – 8 bytes (-9,223,372,036,854,775,808 to 9,223,372,036,854,775,807)

🔸 ulong – System.UInt64 – 8 bytes (0 to 18,446,744,073,709,551,615)

🔸 float – System.Single – 4 bytes (±1.5e−45 to ±3.4e38, 7 digit precision)

🔸 double – System.Double – 8 bytes (±5.0e−324 to ±1.7e308, 15-16 digit precision)

🔸 decimal – System.Decimal – 16 bytes (±1.0 × 10−28 to ±7.9 × 1028, 28-29 digit precision)

🔸 char – System.Char – 2 bytes (U+0000 to U+ffff, UTF16 Unicode character)

🔸 object - System.Object

Подробнее: Built-in types table - C# Reference.

#data_types
Строковый тип в C#

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

Тип string является ссылочным и в платформе .NET (или как упоминалось ранее, в библиотеке BCL) соответствует System.String.

Строки содержат последовательность Unicode (UTF16) символов, являясь эквивалентом массиву элементов типа char, а каждый символ (элемент) занимает ровно 2 байта.

Строковый тип является неизменяемым (immutable), другими словами мы не можем изменить значение строки, не пометив на удаление старое и не создав при этом новое.

Так же нам доступен индексатор (оператор обращения по индексу) [] для доступа к конкретным символам строки.

Небольшой пример работы со строками.

Подробнее про строковый тип: Strings in C# and .NET.

💬 Если факт неизменяемости строк довольно известен, то причины этого порой не так очевидны. Исчерпывающий на мой взгляд ответ по этой теме вы сможете найти по следующей ссылке.

#data_types
Тип object в C#

Все типы платформы .NET являются наследниками System.Object, синонимом которого в языке C# является ключевое слово object.

System.Object является базовым не только для встроенных типов, но и тех, которые мы определяем самостоятельно, а значит экземпляр любого типа может быть приведён к object:

string msg = "A string";
int n = 42;
Person me = new Person("John", 33);

// Can assign anything to an object variable
object o = msg;
o = n;
o = me;


Любой класс который мы определяем в C# также автоматически является наследником от object.

Тип object определяет следующий набор доступных для использования экземплярных методов:

🔸 bool Equals(object)

🔸 void Finalize()

🔸 int GetHashCode()

🔸 Type GetType()

🔸 object MemberwiseClone()

🔸 string ToString()

Статические методы включают в себя:

🔸 bool Equals(object, object)

🔸 bool ReferenceEquals(object, object)

Другими словами, каждый из приведённых выше методов унаследован и автоматически доступен при разработке.

💬 Знаете ли вы, что в 64-битных платформах Garbage Collector не позволяет выделять объекты, размер которых превышает 2 GB и выбрасывает OutOfMemoryException? Помочь с этим нам может специальный элемент конфигурации: gcAllowVeryLargeObjects.

#data_types
Конструкторы по умолчанию у значимых типов в C#

Все значимые (value) типы, встроенные в .NET, поддерживают конструкторы по умолчанию (другими словами, конструкторы без параметров) и могут быть проинициализированы с помощью ключевого слова new. Таким образом, мы можем инициализировать экземпляры значимых типов default'ными для них значениями.

Обычно мы объявляем переменные и сразу же инициализируем их с помощью литералов, однако, ничто не мешает использовать не только new, но и ключевое слово default:

int i;              
int n1 = 12;
int n2 = new int();
int n3 = default(int);

Далее представлены default значения встроенных типов:

🔸 bool type = false

🔸 Numeric types (e.g. int, float) = 0 or 0.0

🔸 char type = single empty character

🔸 DateTime type = 1/1/0001 12:00:00 AM

🔸 string - не значимый тип 😉

На основании замечательной статьи от Jon Skeet - Value types and parameterless constructors стоит также отметить, что:

Операции, которые вызывают parameterless конструктор:

🔸 Использование new int();

🔸 Activator.CreateInstance (обе версии);

Операции, которые не вызывают parameterless конструктор:

🔸 Объявление переменной (неважно какой: локальной, статической, поля);

🔸 Упаковка (boxing);

🔸 Инициализация массива;

🔸 Использование default(T) и new(T) в generic методах;

Больше информации вы сможете найти в уже упомянутой выше статье и по данной ссылке.

#data_types
❇️ На этой неделе компания Microsoft на конференции MSIgnite представила новый портал для обучения: Microsoft Learn.

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

C#:

🔸 C# Intermediate – Static Members, Constants and Extension Methods

🔸 C# Intermediate – Anonymous and Nullable Types

🔸 What is the difference between double? and int? for .Equals comparisons?

🔸 Check List<T> for duplications with optional words to exclude

dotNET:

🔸 Here's what's new and coming to .NET Core with versions 2.1, 2.2, and 3.0

🔸 Just-In-Time (JIT) compilation

🔸 .NET API documentation moved from MSDN to docs.microsoft.com

🔸 Деревья выражений в enterprise-разработке

Common:

🔸 Манифест Чистого Программиста или краткий конспект книги «Чистый Код» Роберта Мартина

Всем хороших и плодотворных выходных 😉

#sof_weekly
​​Иерархия типов в C#

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

Наследование является одним из фундаментальных атрибутов объектно-ориентированного программирования. Оно позволяет определить дочерний класс, который использует (наследует), расширяет или изменяет возможности родительского класса. Класс, члены которого наследуются, называется базовым классом. Класс, который наследует члены базового класса, называется производным классом.

C# и .NET поддерживают только одиночное наследование. Это означает, что каждый класс может наследовать члены только одного класса. Но зато поддерживается транзитивное наследование, которое позволяет определить иерархию наследования для набора типов. Другими словами, тип D может наследовать возможности типа C, который в свою очередь наследует от типа B, который наследует от базового класса A. Благодаря транзитивности наследования члены типа A будут доступны для типа D.

Больше информации и примеров вы сможете найти в следующей статье - Наследование в C# и .NET.

#data_types
MinValue и MaxValue числовых типов в C#

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

byte bMin = byte.MinValue;  
// 0
byte bMax = byte.MaxValue;
// 255 (2^8)

char cMin = char.MinValue;
// 0x0000
char cMax = char.MaxValue;
// 0xffff (2^16)

int nMin = int.MinValue;
// -2147483648
int nMax = int.MaxValue;
// 2147483647 (2^32)

long lMin = long.MinValue;
// -9,223,372,036,854,775,808

long lMax = long.MaxValue;
// 9,223,372,036,854,775,807 (2^64)


Для чего они могут нам понадобиться?

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

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

☝️ Насколько мне известно, прироста производительности от использования типов меньшего размера в циклах в C# замечено не было. Однако, при описании структур данных стоит помнить о том, что такие типы как ushort, short и byte умещаются в меньшее количество бит, а значит более предпочтительны с точки зрения performance и потребления памяти (hold on for a second, это не призыв к действию, а всего лишь напоминание 🙂).

В заключение

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

#data_types
TrueString и FalseString в C#

В продолжении темы о доступных полях встроенных типов сегодня хочу рассказать о Boolean.TrueString и Boolean.FalseString. Эти поля дают возможность использовать строковое представление соответствующих булевых значений в коде.

string trueText = bool.TrueString;   // "True"
string falseText = bool.FalseString; // "False"


Эти поля возвращают "True" и "False" независимо от текущих настроек региона, языка или языка программирования (C# или VB .NET). Аналогичный результат можно получить вызывая метод .ToString() на соответствующем значении:

bool b1 = true;
bool b2 = false;

string true2 = b1.ToString(); // "True"
string false2 = b2.ToString(); // "False"


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

💬 Оказалось, что довольно большое количество разработчиков (больше 90000 просмотров) интересует вопрос: почему Boolean.ToString возвращает "True" а не "true". Причины, на удивление, есть и их несколько. О них вы сможете прочитать уже самостоятельно 😉

#data_types
Создаём объекты с помощью new в C#

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

Принцип его работы достаточно прост:

🔸 Выделяет память под экземпляр типа и инициализирует все поля объекта значениями по умолчанию;

🔸 Вызывает конструктор типа

🔸 Возвращает ссылку на созданный объект в случае если тип ссылочный либо значение, если тип значимый

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

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

// Not instantiated, value is null
Person p1;

// p2 points to new instance of the Person class
// Default constructor, takes no parameters
Person p2 = new Person();

// Construct another Person object using a different constructor,
// which takes Name and Age
Person p3 = new Person("John", 42);


Больше информации и примеров вы сможете найти в следующей статье - new operator.

#data_types
❇️ Выходные на пороге, а значит время очередного еженедельного дайджеста.

Предлагаю вашему внимаю самые 🔥 интересные вопросы этой недели:

🔸 C# Intermediate – Enumerations in C#

🔸 Подводные камни HttpClient в .NET

🔸 Pattern matching в C# 7

🔸 Why does a 'for' loop behave differently when migrating VB.NET code to C#?

🔸 A History of .NET Runtimes

🔸 The danger of TaskCompletionSource<T> class

🔸 Profiling .NET Code with BenchmarkDotNet

🔸 .NET JIT compiler volatile optimizations

🔸 Встреча .Net сообщества на CLRium #4 + онлайн

Всем отличных выходных 😉

#sof_weekly
​​Создание объектов и куча (Heap) в C#

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

Объекты .NET размещаются в области памяти, которая называется управляемой кучей (managed heap), откуда они автоматически удаляются сборщиком мусора, когда наступает "определенный момент в будущем" 🙂. Куча представляет собой непрерывную область памяти, поделённую на занятые и свободные области (блоки) различного размера.

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

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

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

class1 cls1 = new class1();


Оператор new выделяет память под объект типа class1 и располагает его в Heap. В то же время на стеке выделяется память под переменную cls1, которая будет хранить ссылку на создаваемый нами объект. И затем ссылка на только что созданный объект уже присваивается в нашу переменную. Вы можете также заметить, что по завершению выполнения метода Method1 его стек очищается, в то время как объект всё так же присутствует в куче, становясь, тем самым, кандидатом для очистки при следующей сборки мусора.

Больше деталей вы сможете найти по ссылке - автоматическое управление памятью.

#data_types
​​Поведение переменных ссылочных типов в C#

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

public void Method1() {
cls1 obj = new cls1();
cls1 obj1 = obj;
}


Таким образом, если мы изменим значения полей объекта obj, то мы увидим данные изменения и в переменной obj1. Это подтверждает что обе переменных ссылаются на один и тот же объект.

Подробности вы сможете найти по ссылке - Six important .NET concepts: Stack, heap, value types, reference types, boxing, and unboxing.

#data_types
Статические члены классов в C#

Когда мы создаём новый объект и используем переменную ссылочного типа для вызова методов, мы используем экземплярные методы (instance members) и поля объекта, таким образом взаимодействуя с экземпляром проинициализированного ранее типа.

Тем не менее, мы также можем использовать статические методы (static members) и поля класса без непосредственного создания его экземпляра. В таком случае мы оперируем самим типом класса.

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

double dub = -3.14;  
Console.WriteLine(Math.Abs(dub));
Console.WriteLine(Math.Floor(dub));
Console.WriteLine(Math.Round(Math.Abs(dub)));


Далеко ходить за примерами не придётся: вспомните хотя бы классы Console или Math 😉 Мы не создаём их экземпляры каждый раз, когда хотим выполнить математическую операцию или вывести что-либо на экран консоли, достаточо лишь обратиться к ним по именам.

В дальнейшем мы ещё не раз вернёмся к этой теме, а пока что порекомендую ознакомиться со следующим разделом документации - Static Classes and Static Class Members.

💬 Когда использовать статические классы в C# - вопрос не праздный, поэтому ответ на него я вам порекомендую уже в качестве самостоятельного изучения 👨‍💻

#basics
Статические классы в C#

Сегодня хотелось бы чуть подробнее остановиться на теме статических классов.

Класс считается статическим если в его сигнатуре присутствует ключевое слово static, например:

public static class Math { 
// ..
}


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

Более того, на статический класс также накладываются следующие ограничения:

🔸 не может учавствовать в наследовании (служить базовым или являться наследником);

🔸 не может содержать члены с атрибутами доступа protected и protected internal;

🔸 не может иметь переопределённых (override) членов;

🔸 не может содержать конструкторы экземпляров (только static);

Областей применения у статических классов не так много. Они служат для:

🔸 группировки вспомогательных методов (так например Math агрегирует в себе математические операции, Console содержит поля и методы для взаимодействия с консолью, Utils как контейнер для наиболее часто используемых методов вашего приложения);

🔸 определения методов расширения (extension methods).

Хочу также порекомендовать неплохую на мой взгляд русскоязычную заметку по теме - Статика в C#.

#basics
​​❇️ На этой неделе Microsoft поделилась деталями предстоящих обновлений .NET Core 3.0 и .NET Framework 4.8.

Ну а я, по традиции, предлагаю вашему внимаю самые 🔥 интересные статьи и вопросы этой недели:

🔸 C# Intermediate – Inheritance in C#

🔸 .NET Internals - Application execution model

🔸 Can a `Task` context switch before its first `await`?

🔸 What is Clean Code ?

🔸 The Introvert's Guide to Professional Development

🎧 Inversion of Control

Всем замечтательной погоды за окном и отличных выходных!

Понравилась заметка? Тогда поделись ей с другими 😉

#sof_weekly
Числовые преобразования в C#

Ранее я уже писал о числовых типах, которые существуют в языке C#. Сегодня же речь пойдёт об их преобразовании.

В тот момент, когда значение одного типа (допустим int) присваивается переменной другого типа (например long), выполняется преобразование типов.

Значения значимых типов преобразуются (конвертируются) неявно (implicitly) тогда и только тогда, когда отсутствует вероятность потери точности результата (data loss). Это возможно в том случае, когда тип, к которому мы хотим преобразовать наше значение, хранится в памяти в большем или равном количестве бит, как и исходный.

🔸 implicit преобразования:

int i = 12;
long l = i; // Implicit (int to long)
float f = i; // Implicit (int to float)
double d = 4.2f; // Implicit (float to double)


Как видно из кода выше, нам не требуется никаких дополнительных действий для преобразования, ведь long и double располагаются в 64 битах, в то время как int в 32, а значит потеря точности результата нам не грозит.

🔸 explicit преобразования:

long l = 12;
int i = l; // Compiler error


Если мы захотим выполнить аналогичное преобразование в обратном направлении, то компилятор не позволит нам это сделать и сообщит об ошибке на этапе компиляции, как в примере выше.

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

int i = (int)l;   // Explicit (long to int)
float f = 4.2f;
i = (int)f; // Explicit (float to int)
double d = 4.2f;
f = (float)d; // Explicit (double to float)


💬 Позвольте познакомить вас со своим тёзкой, который также имеет богатый практический опыт в разработке программного обеспечения и преподает айтишные предметы в ВУЗе. Почитайте https://yangx.top/tobeITmen, чтобы узнать, каково это - быть айтишником 😉

#data_types
​​Явное приведении значимых типов в C#

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

🔸 Целочисленное к целочисленному - урезание ведущих битов;

🔸 Decimal, float и double к целочисленному - обрезание дробной части и ведущих битов;

🔸 Double к float - округление или Infinity в случае переполнения;

🔸 Float и double к decimal - округление;

🔸 Decimal к float или double - потеря точности;

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

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

💬 На мой взгляд, необходимости заучивать все эти правила нет, однако, помнить - безусловно стоит. Если у вас ещё остались вопросы по преобразованиям или числам с плавающей точкой, то я настоятельно рекомендую вам ознакомиться со следующей темой самостоятельно - Difference between decimal, float and double in .NET?.

#data_types
Статические методы типа System.Char в C#

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

Далее я перечислю лишь некоторые из них:

🔸 char.GetNumericValue

Метод возвращает численное значение символа:

char.GetNumericValue('7');   // 7
char.GetNumericValue('¼'); // 0.25
char.GetNumericValue('Ⅸ'); // 9


🔸 char.GetUnicodeCategory

Метод возвращает UnicodeCategory, к которой относится символ:

char.GetUnicodeCategory('a');            
// LowercaseLetter

char.GetUnicodeCategory('2');
// DecimalDigitNumber

char.GetUnicodeCategory("Upper Case", 6);
// UppercaseLetter


🔸 char.IsControl

Метод возвращает признак того, является ли символ управляющим:

char.IsControl('a');    // false
char.IsControl('\t'); // true


🔸 char.IsDigit

Метод возвращает признак того, является ли символ цифрой:

char.IsDigit('a');      // false
char.IsDigit('¼'); // false
char.IsDigit('3'); // true


🔸 char.IsLetter

Метод возвращает признак того, является ли символ буквой:

char.IsLetter('%');     // false
char.IsLetter('P'); // true


🔸 char.IsLower

Метод возвращает признак того, что символ в нижнем регистре:

char.IsLower('j');      // true
char.IsLower('Y'); // false


🔸 char.IsNumber

Метод возвращает признак того, является ли символ числом:

char.IsNumber('a');     // false
char.IsNumber('¼'); // true


За скобками остались методы проверки символов на пунктуацию, приведения к определённому регистру и другие, найти которые вы сможете в документации к типу Char.

💬 Хотели бы больше информации и общения по C# и dotNET? На днях канал @devdigest опубликовал отличную, на мой взгляд, подборку тематических ресурсов, которой не премину поделиться и с вами.

#data_types
Литералы типа float и суффикс f в C#

Предполагаю, что тема числовых типов уже порядком вам поднадоела, однако, мне ещё есть чем с вами поделиться 🙂

Итак.. давайте проинициализируем несколько переменных типа float целочисленным и дробным значениями:

float f1 = 4;
float f2 = 4.2;


Есть ли на ваш взгляд здесь упущение?

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

Literal of type double cannot be implicitly converted to type 'float'.


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

float f1 = 4;


В данном случае значение 4 является литералом типа int, который, в свою очередь, компилятор сумеет привести к типу float неявно (implicitly) ввиду отсутствия вероятности потери точности. Если этот момент вам не совсем понятен, я предлагаю вам вернуться к следующей заметке и вспомнить правила приведения типов.

Таким образом нам следует запомнить, что согласно спецификации языка C# тип любого целочисленного литерала определяется в зависимости от его значения (int, uint, long, ulong).

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

Любой дробный литерал в исходном коде является значением типа double, а значит мы пытаемся присвоить значение типа double переменной float.

Сделать это компилятор нам позволить не может, ввиду того, что такое приведение типов не может быть осуществлено неявно, а значит, мы должны явно (explicitly) дать понять, что нас это устраивает, следующим образом:

float f2 = (float)4.2;
float f3 = 4.2f;


В завершение мне осталось лишь отметить, что типы float и double являются двоичными типами с плавающей запятой, соответствующими стандарту IEEE 754, а значит значение 4.2 будет представлено как 4.19999980926513671875E0 для float (32 бита) и 4.20000000000000017763568394003E0 для double (64 бита).

💬 Если у вас после прочитанного ещё остались силы и интерес к данной теме, то я порекомендую вам замечательную статью Jon Skeet - Binary floating point and .NET и раздел Literals спецификации языка C# уже в качестве самостоятельного изучения 😉

#data_types
​​❇️ Рабочая неделя заканчивается, а значит пришло время очередной недельной подборки на выходные.

Предлагаю вашему внимаю самые 🔥 интересные статьи и вопросы этой недели:

C#:

🔸 Abstract Classes in C#

🔸 Generics in C#

🔸 Why would one ever use the “in” parameter modifier in C#?

🔸 Producer/Consumer with C# structs?

dotNET:

🔸 How to Get Started with SQL Server and .NET

🔸 Why I cannot create my own analogue of Nullable?

🔸 Looking inside the memory pool

🔸 What is the purpose of public static DateTime ToDateTime(DateTime value)?

Development:

🔸 Когда программный код вызывает восхищение?

🔸 Принципы SOLID, о которых должен знать каждый разработчик

Всем хороших выходных 😉

#sof_weekly
​​Инкремент и декремент в C#

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

Операторы инкремента записывается как два плюса: ++

Существуют два вида инкрементов: преинкремент (или префиксный инкремент) и постинкремент (или постфиксный инкремент). В синтаксисе префиксный инкремент ставится перед необходимой переменной, а постфиксный, соответственно, после.

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

int n1 = 5;
int n2 = 2 * ++n1; // n2 now 12, n1 is 6


А при использовании операции постинкремента значение переменной сначала используется в выражении, а потом увеличивается на 1:

int n1 = 5;
int n2 = 2 * n1++; // n2 now 10, n1 is 6


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

Операторы декремента записывается как два минуса: --

Декремент также имеет два вида: предекремент (префиксный декремент) и постдекремент (постфиксный декремент).

int n1 = 5;
int n2 = 2 * n1--; // n2 now 10, n1 is 4


Вот небольшое задание для проверки.

💬 А знаете ли вы, что синий значёк говорит нам о том, что выполнение продолжилось уже в другом потоке? 😉

#basics