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

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

admin - @haarrp
加入频道
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
Арифметические операции в C#

Во вчерашней заметке мы рассмотрели унарные операции инкремента и декремента.

Сегодняшняя тема достаточно простая, однако, обойти её стороной, на мой взгляд, было бы неправильно. Итак.. бинарные арифметические операции в C#:

🔸 + - сложение двух чисел:

int x = 10;
int z = x + 12; // 22


🔸 - - вычитание двух чисел:

int x = 10;
int z = x - 6; // 4


🔸 * - умножение двух чисел:

int x = 10;
int z = x * 5; // 50


🔸 / - деление двух чисел:

int x = 10;
int z = x / 5; // 2

double a = 10;
double b = 3;
double c = a / b; // 3.33333333


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

double z = 10 / 4; // 2


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

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

double z = 10.0 / 4.0; // 2.5


🔸 % - остаток от целочисленного деления:

double x = 10.0;
double z = x % 4.0; // 2


💬 Помните ли вы порядок выполнения операторов? Вот небольшое задание для проверки 😉

#basics
Целочисленное деление и округление в C#

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

int n1 = 7 / 2;       // 3
long n2 = -7 / 2; // -3
short n3 = -11 / -3; // 3


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

При попытке поделить на значение, равное 0, мы получим исключение System.DivideByZeroException в runtime:

int i = 0;
int r = 7 / i; // DivideByZeroException


При попытке поделить на литерал 0 мы получим исключение на этапе компиляции:

int r = 7 / 0; // Division by constant zero


💬 Тем удивительнее оказывается тот факт, что в случае деления числа с плавающей точкой на ноль (1.0 / 0) вышеупомянутое исключение выброшено не будет. Мы просто получим в результате бесконечность (Infinity) 🙂

#basics
Округление чисел с плавающей точкой в C#

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

int n1 = 4.8f;  // Cannot implicitly convert


Поэтому в дело вступает явное приведение:

int n1 = (int)4.8f;


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

Если же мы хотим руководствоваться математическими правилами округления, то с этим нам поможет класс System.Convert:

float f1 = 4.8f;
int n1 = Convert.ToInt32(4.8f); // 5


Но и здесь всё не всегда так гладко 😅 Оказывается, в .NET алгоритм округления (banker's rounding) отличается от привычного нам в тех случаях, когда значения являются пограничными: 0.5, 3.5. В этих случаях округление осуществляется в пользу ближайшего чётного:

int n1 = Convert.ToInt32(8.5f);  // 8
int n2 = Convert.ToInt32(9.5f); // 10


💬 Заинтересованы алгоритмом и причиной подобного решения в .NET? Подробнее почитать об этом вы сможете уже самостоятельно здесь 😉

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

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

🔸 C# Intermediate – Queue, Stack, And Hashtable in C#

🔸 What does .NET's Equals method really mean?

🔸 Use structures to improve the readability of your code

🔸 Playing with C# 7 - Deconstruct

🔸 The Evolution of C#

🔸 IEnumerable<T> and .Where Linq method behaviour?

🔸 How to find all classes that implements a generic abstract class using reflection in C#?

🔸 Why are 1000 threads faster than a few?

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

#sof_weekly
Переполнение в C#

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

Такая ситуация называется переполнением (overflow) и корнями уходит к арифметике и битовому представлению чисел в computer science.

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

Позвольте мне продемонстрировать это поведение на примерах:

🔸 Максимальным значением для беззнакового типа uint является 0xffffffff:

uint u1 = 0xffffffff;
u1 = u1 + 5; // 0x00000004 (перенос)


🔸 int.MaxValue - максимальное значение для знакового типа int:

int n1 = int.MaxValue;
n1 = n1 + 1; // -2147483648 (перенос)


🔸 Минимальное значение для знакового типа short является -32768:

short s1 = short.MinValue;
s1 = (short)(s1 - 1); // 32767 (перенос)


💬 Как я уже упоминал ранее, использовать минимальные и максимальные значения в циклах стоит с осторожностью, иначе это может привести к неожиданным последствиям. Примером тому служит следующий код. Что выведется на экране? Ответ вы сможете найти ниже на странице 😉

#data_types
​​Контроль переполнения в C#

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

int n1 = int.MaxValue;
n1 = n1 + 1; // -2147483648 (перенос)


Однако это поведение может быть изменено и для этого мы можем воспользоваться ключевым словом checked:

int n1 = int.MaxValue;
n1 = checked(n1 + 1); // OverflowException


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

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

checked
{
int n1 = int.MaxValue;
n1 = n1 + 1; // OverflowException
}


Также стоит упомянуть о возможности изменения поведения при переполнении в настройках проекта: Project > Properties > Build > Advanced > Check for arithmetic overflow/underflow.

#data_types
Выключение контроля переполнения в C#

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

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

int n1 = int.MaxValue;      // 2147483647 (0x7FFFFFFF)
int n2 = unchecked(n1 + 1); // Перенос -2147483648
int n3 = n1 + 1; // OverflowException


Как и в случае с checked мы так же может использовать unchecked не только для конкретных операторов, но и целых участков кода:

int n1 = int.MaxValue;  // 2147483647
unchecked
{
int n2 = n1 + 1; // Перенос -2147483648
int n4 = n1 * 2; // -2
}


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

int n1 = int.MaxValue + 1;  // Ошибка компиляции: overflow


В этом случае нам также может помочь unchecked:

int n2 = unchecked(int.MaxValue + 1);


💬 Одной из полезных возможностей в Visual Studio для повышения продуктивности для меня являются сниппеты (настоятельно рекомендую ознакомиться всем тем, кто их не использует 😉). А какой из них вы используете чаще всего?

#data_types