Поведение переменных ссылочных типов в C#
Как вы уже знаете, переменные значимых типов хранят в себе непосредственно значение, в то время как переменные ссылочных типов лишь ссылку на объект. В связи с этим нам необходимо помнить о том, что при присваивании значения одной ссылочной переменной другой, обе они в итоге начинают ссылаться на один и тот же объект в куче:
Таким образом, если мы изменим значения полей объекта
Подробности вы сможете найти по ссылке - Six important .NET concepts: Stack, heap, value types, reference types, boxing, and unboxing.
#data_types
Как вы уже знаете, переменные значимых типов хранят в себе непосредственно значение, в то время как переменные ссылочных типов лишь ссылку на объект. В связи с этим нам необходимо помнить о том, что при присваивании значения одной ссылочной переменной другой, обе они в итоге начинают ссылаться на один и тот же объект в куче:
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) и поля класса без непосредственного создания его экземпляра. В таком случае мы оперируем самим типом класса.
Статический класс по большей части такой же, как и нестатический, однако, имеется одно значительное отличие: экземпляры статического класса создавать нельзя. Другими словами, мы не сможем использовать ключевое слово
Далеко ходить за примерами не придётся: вспомните хотя бы классы
В дальнейшем мы ещё не раз вернёмся к этой теме, а пока что порекомендую ознакомиться со следующим разделом документации - Static Classes and Static Class Members.
💬 Когда использовать статические классы в C# - вопрос не праздный, поэтому ответ на него я вам порекомендую уже в качестве самостоятельного изучения 👨💻
#basics
Когда мы создаём новый объект и используем переменную ссылочного типа для вызова методов, мы используем экземплярные методы (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#
Сегодня хотелось бы чуть подробнее остановиться на теме статических классов.
Класс считается статическим если в его сигнатуре присутствует ключевое слово
Такой класс содержит только статические члены (поля, методы, свойства и т.д.) и явно создать его экземпляр с помощью ключевого слова
Более того, на статический класс также накладываются следующие ограничения:
🔸 не может учавствовать в наследовании (служить базовым или являться наследником);
🔸 не может содержать члены с атрибутами доступа
🔸 не может иметь переопределённых (
🔸 не может содержать конструкторы экземпляров (только
Областей применения у статических классов не так много. Они служат для:
🔸 группировки вспомогательных методов (так например
🔸 определения методов расширения (extension methods).
Хочу также порекомендовать неплохую на мой взгляд русскоязычную заметку по теме - Статика в C#.
#basics
Сегодня хотелось бы чуть подробнее остановиться на теме статических классов.
Класс считается статическим если в его сигнатуре присутствует ключевое слово
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# 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#. Сегодня же речь пойдёт об их преобразовании.
В тот момент, когда значение одного типа (допустим
Значения значимых типов преобразуются (конвертируются) неявно (implicitly) тогда и только тогда, когда отсутствует вероятность потери точности результата (data loss). Это возможно в том случае, когда тип, к которому мы хотим преобразовать наше значение, хранится в памяти в большем или равном количестве бит, как и исходный.
🔸 implicit преобразования:
Как видно из кода выше, нам не требуется никаких дополнительных действий для преобразования, ведь
🔸 explicit преобразования:
Если мы захотим выполнить аналогичное преобразование в обратном направлении, то компилятор не позволит нам это сделать и сообщит об ошибке на этапе компиляции, как в примере выше.
В таком случае мы будем вынуждены производить преобразования уже явно:
💬 Позвольте познакомить вас со своим тёзкой, который также имеет богатый практический опыт в разработке программного обеспечения и преподает айтишные предметы в ВУЗе. Почитайте https://yangx.top/tobeITmen, чтобы узнать, каково это - быть айтишником 😉
#data_types
Ранее я уже писал о числовых типах, которые существуют в языке 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#
Выполняя явные преобразования числовых типов мы можем столкнуться с ситуацией, при которой исходное значение не может корректно отобразиться на требуемый нам тип. В таком случае дальнейшие выполнение приведения осуществляется по следующим правилам:
🔸 Целочисленное к целочисленному - урезание ведущих битов;
🔸
🔸
🔸
🔸
Также не стоит забывать и об операторе
Подробнее с правилами явного приведения вы сможете ознакомиться уже в документации.
💬 На мой взгляд, необходимости заучивать все эти правила нет, однако, помнить - безусловно стоит. Если у вас ещё остались вопросы по преобразованиям или числам с плавающей точкой, то я настоятельно рекомендую вам ознакомиться со следующей темой самостоятельно - Difference between decimal, float and double in .NET?.
#data_types
Выполняя явные преобразования числовых типов мы можем столкнуться с ситуацией, при которой исходное значение не может корректно отобразиться на требуемый нам тип. В таком случае дальнейшие выполнение приведения осуществляется по следующим правилам:
🔸 Целочисленное к целочисленному - урезание ведущих битов;
🔸
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.GetNumericValue
Метод возвращает численное значение символа:
🔸 char.GetUnicodeCategory
Метод возвращает UnicodeCategory, к которой относится символ:
🔸 char.IsControl
Метод возвращает признак того, является ли символ управляющим:
🔸 char.IsDigit
Метод возвращает признак того, является ли символ цифрой:
🔸 char.IsLetter
Метод возвращает признак того, является ли символ буквой:
🔸 char.IsLower
Метод возвращает признак того, что символ в нижнем регистре:
🔸 char.IsNumber
Метод возвращает признак того, является ли символ числом:
За скобками остались методы проверки символов на пунктуацию, приведения к определённому регистру и другие, найти которые вы сможете в документации к типу Char.
💬 Хотели бы больше информации и общения по C# и dotNET? На днях канал @devdigest опубликовал отличную, на мой взгляд, подборку тематических ресурсов, которой не премину поделиться и с вами.
#data_types
Тип
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#
Предполагаю, что тема числовых типов уже порядком вам поднадоела, однако, мне ещё есть чем с вами поделиться 🙂
Итак.. давайте проинициализируем несколько переменных типа
Есть ли на ваш взгляд здесь упущение?
С точки зрения разработчика - всё в порядке, тип
Прежде чем я объясню почему так происходит, давайте разберём первую строку и вспомним правила приведения типов:
В данном случае значение
Таким образом нам следует запомнить, что согласно спецификации языка C# тип любого целочисленного литерала определяется в зависимости от его значения (
Отлично, надеюсь с этим разобрались. Так а что же со второй строкой? На самом деле- всё просто, и вот вторая вещь, которую нам стоит запомнить:
Любой дробный литерал в исходном коде является значением типа
Сделать это компилятор нам позволить не может, ввиду того, что такое приведение типов не может быть осуществлено неявно, а значит, мы должны явно (explicitly) дать понять, что нас это устраивает, следующим образом:
В завершение мне осталось лишь отметить, что типы
💬 Если у вас после прочитанного ещё остались силы и интерес к данной теме, то я порекомендую вам замечательную статью Jon Skeet - Binary floating point and .NET и раздел Literals спецификации языка C# уже в качестве самостоятельного изучения 😉
#data_types
Предполагаю, что тема числовых типов уже порядком вам поднадоела, однако, мне ещё есть чем с вами поделиться 🙂
Итак.. давайте проинициализируем несколько переменных типа
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#:
🔸 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#
Инкремент – это операция, которая увеличивает переменную на единицу, если переменная числовая, и возвращает следующий символ из таблицы символов, если переменная символьного типа (
Операторы инкремента записывается как два плюса:
Существуют два вида инкрементов: преинкремент (или префиксный инкремент) и постинкремент (или постфиксный инкремент). В синтаксисе префиксный инкремент ставится перед необходимой переменной, а постфиксный, соответственно, после.
Главное различие между ними, что при использовании операции преинкремента значение переменной сначала увеличивается на 1, а затем используется в выражении, к которому относится данная переменная:
А при использовании операции постинкремента значение переменной сначала используется в выражении, а потом увеличивается на 1:
Декремент – это подобная инкременту операция, с той лишь разницей, что она уменьшает числовую переменную на единицу, а для символьной переменной выбирает предшествующий ей символ из таблицы символов.
Операторы декремента записывается как два минуса:
Декремент также имеет два вида: предекремент (префиксный декремент) и постдекремент (постфиксный декремент).
Вот небольшое задание для проверки.
💬 А знаете ли вы, что синий значёк говорит нам о том, что выполнение продолжилось уже в другом потоке? 😉
#basics
Инкремент – это операция, которая увеличивает переменную на единицу, если переменная числовая, и возвращает следующий символ из таблицы символов, если переменная символьного типа (
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#:
🔸 + - сложение двух чисел:
🔸 - - вычитание двух чисел:
🔸 * - умножение двух чисел:
🔸 / - деление двух чисел:
При делении стоит учитывать, что если оба операнда представляют целые числа, то результат также будет округляться до целого числа:
Хочу обратить ваше внимание на то, что несмотря на тип переменной
Для выхода из этой ситуации необходимо определять литералы или переменные, участвующие в операции, именно как типы
🔸 % - остаток от целочисленного деления:
💬 Помните ли вы порядок выполнения операторов? Вот небольшое задание для проверки 😉
#basics
Во вчерашней заметке мы рассмотрели унарные операции инкремента и декремента.
Сегодняшняя тема достаточно простая, однако, обойти её стороной, на мой взгляд, было бы неправильно. Итак.. бинарные арифметические операции в 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#
При делении одного целочисленного значения на другое с помощью оператора деления
Причину этого поведения я описывал в предыдущей заметке - целочисленные аргументы приводят к целочисленному результату.
При попытке поделить на значение, равное
При попытке поделить на литерал
💬 Тем удивительнее оказывается тот факт, что в случае деления числа с плавающей точкой на ноль (
#basics
При делении одного целочисленного значения на другое с помощью оператора деления
/
результат всегда округляется до нуля. Другими словами- обрезается: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#
Во время разработки мы временами сталкиваемся с необходимостью округлить число с плавающей точкой типа
Поэтому в дело вступает явное приведение:
Однако, с этим кодом всё не так просто. Дело в том, что подобное округление на деле окажется ничем иным, как отбрасыванием дробной части у целочисленного значения.
Если же мы хотим руководствоваться математическими правилами округления, то с этим нам поможет класс System.Convert:
Но и здесь всё не всегда так гладко 😅 Оказывается, в .NET алгоритм округления (banker's rounding) отличается от привычного нам в тех случаях, когда значения являются пограничными: 0.5, 3.5. В этих случаях округление осуществляется в пользу ближайшего чётного:
💬 Заинтересованы алгоритмом и причиной подобного решения в .NET? Подробнее почитать об этом вы сможете уже самостоятельно здесь 😉
#basics
Во время разработки мы временами сталкиваемся с необходимостью округлить число с плавающей точкой типа
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# 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 при переполнении ведущие биты обнуляются, после чего результат "умещается" в доступных битах. В случае с беззнаковыми типами - большие значения станут меньше, а при переполнении знаковых типов положительные числа станут отрицательными.
Позвольте мне продемонстрировать это поведение на примерах:
🔸 Максимальным значением для беззнакового типа
🔸
🔸 Минимальное значение для знакового типа
💬 Как я уже упоминал ранее, использовать минимальные и максимальные значения в циклах стоит с осторожностью, иначе это может привести к неожиданным последствиям. Примером тому служит следующий код. Что выведется на экране? Ответ вы сможете найти ниже на странице 😉
#data_types
Во время выполнения арифметических операций с целочисленными типами возможна ситуация, при которой итоговый результат выходит за рамки доступных значений результирующего типа (другими словами не может быть корректно представлен и размещён в памяти).
Такая ситуация называется переполнением (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#
В прошлой заметке я уже упоминал о том, что по умолчанию при переполнении ведущие биты обнуляются, после чего результат "умещается" в доступных битах:
Однако это поведение может быть изменено и для этого мы можем воспользоваться ключевым словом
В таком случае вместо продолжения выполнения программы будет выброшено исключение
Синтаксис ключевого слова
Также стоит упомянуть о возможности изменения поведения при переполнении в настройках проекта:
#data_types
В прошлой заметке я уже упоминал о том, что по умолчанию при переполнении ведущие биты обнуляются, после чего результат "умещается" в доступных битах:
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#
В предыдущей заметке я рассказывал о ключевом слове
В том случае, если в настройках проекта в качестве поведения по умолчанию мы выбираем опцию выбрасывать исключения в случае переполнения, то
Как и в случае с
Уверен, вы замечали, что следующий код не будет скомпилирован и отобразится ошибка:
В этом случае нам также может помочь
💬 Одной из полезных возможностей в Visual Studio для повышения продуктивности для меня являются сниппеты (настоятельно рекомендую ознакомиться всем тем, кто их не использует 😉). А какой из них вы используете чаще всего?
#data_types
В предыдущей заметке я рассказывал о ключевом слове
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
Parse чисел в C#
Каждый числовый тип в C# содержит метод
Однако, важно отметить, что результат выполнения этого метода может обернуться для нас и следующими исключениями:
🔸 FormatException:
В этом примере мы пробуем привести (распарсить) дробное значение к типу
🔸 OverflowException:
В данном случае мы пытаемся привести отрицательное число к типу
💬 Одним из полезных атрибутов, помогающем как в документации, так и в контроле поведения, является
#strings
Каждый числовый тип в C# содержит метод
Parse
, с помощью которого мы можем преобразовывать строки в соответствующие числовые значения:byte b1 = byte.Parse("200");
sbyte sb1 = sbyte.Parse("-100");
float f1 = float.Parse("1.2e-4");
Однако, важно отметить, что результат выполнения этого метода может обернуться для нас и следующими исключениями:
🔸 FormatException:
int n1 = int.Parse("3.4"); // FormatException
В этом примере мы пробуем привести (распарсить) дробное значение к типу
int
, в результате чего получаем исключение о некорректности формата входного параметра 3.4
.🔸 OverflowException:
uint ui1 = uint.Parse("-1"); // OverflowException
В данном случае мы пытаемся привести отрицательное число к типу
uint
, значения которого могут быть только положительными. Как итог - исключение, сигнализирующее о переполнении.💬 Одним из полезных атрибутов, помогающем как в документации, так и в контроле поведения, является
ObsoleteAttribute
. С его помощью мы можем помечать элементы программы, которые больше не должны использоваться и вскоре могут быть удалены. Обычно мы получаем предупреждение, однако, знали ли вы, что это поведение настраиваемое и мы можем даже возвращать полноценную ошибку компиляции 🙂?#strings
❇️ Рабочая неделя заканчивается, а значит пришло время очередной недельной подборки на выходные.
Предлагаю вашему внимаю самые 🔥 интересные статьи и вопросы этой недели:
🔸 Why does Enumerable.Single() iterate all elements, even when more than one item has already been found?
🔸 How to use Factory Method Design Pattern in C#
🔸 C# Intermediate – Delegates in C#
🔸 How to properly implement an interface that was designed for async usage?
🔸 Secure Random Integers in .NET Core 3
🔸 .NET Standard vs. .NET Core
Всем отличных выходных 😉
#sof_weekly
Предлагаю вашему внимаю самые 🔥 интересные статьи и вопросы этой недели:
🔸 Why does Enumerable.Single() iterate all elements, even when more than one item has already been found?
🔸 How to use Factory Method Design Pattern in C#
🔸 C# Intermediate – Delegates in C#
🔸 How to properly implement an interface that was designed for async usage?
🔸 Secure Random Integers in .NET Core 3
🔸 .NET Standard vs. .NET Core
Всем отличных выходных 😉
#sof_weekly
Конкатена́ция строк в C#
В C# мы можем использовать оператор
Мы можем использовать этот оператор неограниченное количество раз в рамках одного выражения (expression), а само выражение использовать в тех местах кода, где ожидается строка:
Более того, специальные методы String.Concat и String.Format содержат дополнительные перегрузки, которые также могут быть использованы для конкатенации:
💬 Продолжая рассказывать про полезные фичи в C# нельзя не упомянуть coalesce оператор
#strings
В C# мы можем использовать оператор
+
не только для сложения чисел, но и склеивания (конкатенации) строк:string s1 = "C#";
string s2 = "fun";
string s3 = s1 + " is " + s2; // "C# is fun"
Мы можем использовать этот оператор неограниченное количество раз в рамках одного выражения (expression), а само выражение использовать в тех местах кода, где ожидается строка:
string s1 = "Hello " + " Wor" + "ld";
Console.WriteLine("Wish " + "you " + "the best");
Более того, специальные методы String.Concat и String.Format содержат дополнительные перегрузки, которые также могут быть использованы для конкатенации:
// Concat method
string s4 = String.Concat(new object[] {
"The ", 3, " musketeers"
});
string s5 = String.Concat("This", "That");
// Use String.Format to concatenate
string s6 = string.Format("{0}{1}{2}", s1, " is ", s2);
💬 Продолжая рассказывать про полезные фичи в C# нельзя не упомянуть coalesce оператор
??
. Принцип его работы прост- возвращать left-hand операнд если он не null
и right-hand в обратном случае: int y = x ?? -1
. Берите на вооружение 😉#strings
Forwarded from SeasonedDev
Dear friend,
If you read this, then you, like me, are clearly passionate about programming and tech 💻
My name is Maxim and I'm happy to meet you here ✌️
I hope that what I post on this channel will take your software development skills to the next level.
💬 If you have any questions, then you can always contact me via the @webdev_en chat.
See you at the new channel: @seasoneddev
If you read this, then you, like me, are clearly passionate about programming and tech 💻
My name is Maxim and I'm happy to meet you here ✌️
I hope that what I post on this channel will take your software development skills to the next level.
💬 If you have any questions, then you can always contact me via the @webdev_en chat.
See you at the new channel: @seasoneddev