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

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

admin - @haarrp
加入频道
Идентификаторы и правила их именования в C#

Под идентификатором (identifier) в языке C# считаются любые имена, которые мы используем в именах переменных, классов, интерфейсов, методов, свойств, пространств имён и так далее.

На именование идентификаторов накладываются следующие ограничения:

🔸 Case sensitive - name != Name;

🔸 Не могут начинаться с цифр;

🔸 Не могут содержать пробелы;

🔸 Могут включать в себя Unicode символы: string имя = ""; и class 国家 { } вполне себе валидные идентификаторы, но делать так без необходимости противопоказано ⛔️ То же относится и к именованию файлов в вашем проекте;

🔸 Не могут совпадать с ключевыми словами (keywords) языка;

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

class @class
{
int @int;
string @default;
}


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

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

🔸 Джеффри Рихтер о том, как кодить, писать книги и создавать свои компании

🔸 C# Back to Basics – Arrays in C#

🔸 What function will be called?

🔸 Enumeration in .NET V — ToList() or not ToList()?

🔸 How to round to nearest even integer?

Всем удачных выходных 😉

#sof_weekly
Контекстные (Contextual) ключевые слова C#

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

var x = "y";
dynamic m = "n";
string t = nameof(x);


В противном случае- они ничем не отличаются от обычных идентификаторов.

Вот их неполный список: async, dynamic, global, join, value, await, select, var, get, nameof, set и другие.

Более того, они не являются зарезервированными, а значит их можно (но так же не рекомендуется ⛔️) использовать в качестве имён без специального символа @ следующим образом:

var var = "";
bool async = false;
string dynamic = "";
int get = 1;


Стоит заметить, что кроме contextual keywords так же в языке существуют следующие группы ключевых слов:

🔸 Statement keywords;

🔸 Operator keywords;

🔸 Conversion keywords;

🔸 Access keywords;

🔸 Literal keywords;

🔸 Query keywords;

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

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

#basics
Что такое литералы (Literals) ключевые слова C#

Литерал - это способ представления значения в исходном коде. Чаще всего мы используем их при инициализации переменных и констант, передаче в качестве аргументов в вызовы методов, форматировании и проверке значений на равенство.

🔸 Boolean - true / false;

🔸 Integer - 12, 24L, 100UL, 0x7DC;

🔸 Floating-point - 1.0, 2.2F, 6.02E23, 123.45m;

🔸 Character - 'b', 'Y', '!', '\n', '\x04DA';

🔸 String - "C#", "Look Ӛ", "Line 1\nLine 2";

🔸 Null - null;

Начиная с C# 7.0 в нашем распоряжении так же появился не только новый вид литерала:

🔸 Binary - 0b00001111, 0b1111000011110000;

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

ushort s1 = 0b1011_1100_1011_0011;
int x1 = 0x44aa_abcd;


Чуть более подробно можно почитать в этой замечательной статье.

💬 А знаете ли вы, что кроме стандартного объявления индексатора:

public string this[string key] {
get { return internalDictionary[key]; }
}

// Usage: something["x"]


Мы можем также использовать params:

public string this[params string[] keys] {
get { return internalDictionary[key]; }
}

// Usage: something["x", "y", "z"]


Ни разу не пригодилось, но крайне любопытно 😉

#basics
Булевы (Boolean) литералы в C#

Булевы литералы или, говоря проще, логические значения "Истина" (да) и "Ложь" (нет) представлены ключевыми словами true и false.

В исходном коде чаще всего используется ключевое слово bool, являющееся на деле alias для структуры System.Boolean:

bool isValid = true;
bool hasErrors = false;


Значением по умолчанию является false.

💬 Задумывались ли вы, почему в языке C# тип System.Boolean занимает 1 байт? Ведь для кодирования значений 0 и 1 достаточно всего лишь одного бита.

Если вам интересно, почему же так происходит, то по этой ссылке вы узнаете ответ 😉

#basics
Выражения, операторы и операнды в C#

Ранее мы уже рассматривали литералы и типы данных. Сегодня предлагаю определить такие понятия как выражения, операторы и операнды.

Чаще всего выражение состоит из оператора, операндов и присваивания результата выполнения и выглядит следующим образом:

int result = 1 + 2;


В данном примере "+" является оператором сложения, который применяется к двум операндам (1 и 2), результат выполнения которого присваивается оператором "=" переменной типа int.

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

Говоря про оператор присваивания можно заметить, что он достаточно прост и принимает два операнда. Left hand операнд является переменной, которой будет присвоен right hand операнд.

x = 10;
// = - operator
// x - left hand operand
// 10 - right hand operand

x = y + z;
// = - operator
// x - left hand operand
// (y + z) - right hand operand
// + - operator
// y - left hand operand
// z - right hand operand


Операторы присваивания и сложения это далеко не все доступные операторы. На данный момент их количество в языке C# составляет уже более 42 штук.

💯 Дорогие подписчики, сегодня, в 256 день в году, поздравляю всех вас с днём программиста 🍰 А наш канал, со своей первой важной отметкой в 100 читателей. Спасибо что вы с нами!

#basics
❇️ Всех ещё раз с прошедшим днём программиста! А сегодня уже пятница, а значит время очередной еженедельной подборки по C# и .NET.

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

🔸 What's Coming to C#?

🔸 Keynote from the people who build .NET

🔸 C# Back to Basics – Working With Files, File and Directory

🔸 How to determine the size of var?

Всем удачных выходных 😉

#sof_weekly
Порядок выполнения операторов в C#

Каждый оператор в языке C# характеризуется приоритетом, в котором он будет выполнен при исполнении выражения.

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

Далее представлены группы операторов, отсортированные по приоритету (в рамках группы приоритет одинаковый):

🔸 Pimary:  x.y  f(x)  a[x]  x++  x–  new  typeof  checked  unchecked

🔸 Unary: + – ! ~ ++x –x (T)x

🔸 Multiplicative: * / %

🔸 Additive: + –

🔸 Shift: << >>

🔸 Relational: < > <= >= is as

🔸 Equality: == !=

🔸 Logical AND: &

🔸 Logical XOR: ^

🔸 Logical OR: |

🔸 Conditional AND: &&

🔸 Conditional OR: ||

🔸 Conditional: ?:

🔸 Assignment: = *= /= %= += -= <<= >>= &= ^= |=


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

#basics
Комментарии в C#

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

Есть 2 способа это сделать:

🔸 Однострочный комментарий - начинается с двойных слешей

// Adds two integers and returns the result
public static int Add(int a, int b) {

}


🔸 Многострочный комментарий - окружён с обоих сторон слешом и звёздочкой

/*
The main Math class
Contains all methods for performing basic math functions
*/
public class Math {

}


Отдельно стоит упомянуть так же XML Comments (подробнее), которые благодаря стандартизации в дальнейшем используются не только при подсказках IntelliSense, но и являются источником данных при генерации документации на основании кода (например в Swagger):

/// <summary>  
/// This class performs an important function.
/// </summary>
public class MyClass {}


☝️ Полезными в данном случае горячими клавишами при разработке являются Ctrl+K, Ctrl+C (закомментировать выделенное) и Ctrl+K, Ctrl+U (раскомментировать выделенное).

Подробнее по теме порекомендую почитать следующую статью: комментирование кода - хорошие, плохие и отвратительные комментарии.

#basics
Типы, переменные, значения, экземпляры и выражения в C#

В языке C# тип (например int) определяет каким будет значение (42) хранящееся в переменной.

Переменная является хранилищем данных. Каждая переменная может содержать значение конкретного типа (экземпляр), которое может меняться во время исполнения программы.

Константы являются переменными, значения которых неизменны и определены уже на этапе компиляции. Они так же хранят в себе значения (экземпляры) конкретных типов.

Выражения возвращают конкретные значения по итогу их успешного исполнения. У выражений так же есть конкретный тип.

В языке C# доступны не только большое кол-во встроенных типов (например int, float), но и конструкции, позволяющие описывать свои собственные типы (class, enum, struct).

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

#data_types
​​Value и reference типы в языке C#

Значимые и ссылочные типы являются по праву одними из базисов языка C#, а вопрос об их отличиях, как следствие, одним из самых избитых на собеседованиях, поэтому сегодня я постараюсь ответить на него со всеми подробностями:

Значимые типы:

🔸 Унаследованы от System.ValueType (которые, в свою очередь, от System.Object)

🔸 Переменная хранит непосредственно значение, а в случае присваивания его копию

🔸 В качестве аргументов передаются по значению (копируются)

🔸 Не очищаются сборщиком мусора, не имеют деструктор и финализатор

🔸 Объявляются как структура (struct) или перечисление (enum)

🔸 Объявлены с модификатором sealed, а значит не могут выступать базовыми типами для наследников

Ссылочные типы:

🔸 Унаследованы от System.Object или иного ссылочного типа

🔸 Память под экземпляры выделяется в управляемой куче (managed heap)

🔸 Переменная хранит адрес (указатель) на участок памяти в управляемой куче, где уже хранится непосредственно значение (или null)

🔸 В качестве аргументов передаются по ссылке (указатель)

🔸 Очищаются сборщиком мусора

🔸 Объявляются как класс (class), делегат (delegate) или интерфейс (interface)

🔸 Поддерживают наследование

Одной из лучших и наиболее ёмких, на мой взгляд, статей по этой теме является Memory in .NET - what goes where от Jon Skeet'а.

💬 Я намеренно не стал приводить информацию о том, что значимые типы хранятся в стеке (потока выполнения). Дело в том, что это не совсем так. Подробнее про детали реализации value type и механизм выделения памяти для значений можно почитать в статье Eric Lipert'а: The Truth About Value Types.

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

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

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

C#:

🔸 C# Intermediate – Properties in C#

🔸 C# 8 Small Features

🔸 Null coalescing operator IList, Array, Enumerable.Empty in foreach

🔸 C# Asynchronous with task is slower than synchronous

Debugging:

🔸 Отладка как процесс

🔸 Breakpoints FAQ: How can I pause my code in Visual Studio?

Performance:

🔸 Struct и readonly: как избежать падения производительности

🔸 Performance of value-type vs reference-type enumerators

Common:

🔸 Как мы создали технологичный продукт и провалились на дно

🔸 23 рекомендации для читабельного кода

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

#sof_weekly
Встроенные типы и 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