.NET Разработчик
6.51K subscribers
427 photos
2 videos
14 files
2.04K links
Дневник сертифицированного .NET разработчика.

Для связи: @SBenzenko

Поддержать канал:
- https://boosty.to/netdeveloperdiary
- https://patreon.com/user?u=52551826
- https://pay.cloudtips.ru/p/70df3b3b
Download Telegram
День двести двадцать шестой. #BestPractices
Советы по разработке членов типов
Разработка событий
События являются наиболее часто используемой формой обратных вызовов. Существует две группы событий: события, возникающие до изменения состояния системы, называемые пред-событиями, и события, возникающие после изменения состояния, называемые пост-событиями. Например, пред-событие Form.Closing вызывается перед закрытием формы, пост-событие Form.Closed вызывается после закрытия формы.

ИСПОЛЬЗУЙТЕ термин raise для событий вместо fire или trigger.
ИСПОЛЬЗУЙТЕ System.EventHandler<TEventArgs> вместо того, чтобы вручную создавать новые делегаты для использования в качестве обработчиков событий.
⚠️ РАССМОТРИТЕ использование классов, производных от EventArgs, в качестве аргумента события, кроме случаев, когда вы абсолютно уверены, что событию никогда не потребуется передавать какие-либо данные в метод обработки, и в этом случае вы можете напрямую использовать тип EventArgs. Если вы напрямую используете EventArgs, вы никогда не сможете добавить какие-либо данные для передачи в обработчик события без нарушения совместимости. Если вы используете подкласс, даже если изначально он полностью пуст, вы сможете добавлять свойства в подкласс при необходимости.
ИСПОЛЬЗУЙТЕ защищенный виртуальный метод, для вызова каждого события. Это применимо только к нестатическим событиям в открытых классах, но не к структурам, закрытым классам или статическим событиям. Цель такого метода - предоставить производному классу способ обработки события с использованием переопределения. Переопределение - это более гибкий, быстрый и более естественный способ обработки событий базового класса в производных классах. По соглашению имя метода должно начинаться с On и сопровождаться названием события. Производный класс может не вызывать базовую реализацию метода в переопределении. Будьте готовы к этому, не включая в базовый метод код который требуется для корректной работы базового класса.
ИСПОЛЬЗУЙТЕ один параметр для передачи аргументов события. Параметр должен быть назван e и должен иметь тип класса аргументов события.
ИЗБЕГАЙТЕ передачи null в качестве параметра отправителя при возникновении нестатического события. Но используйте null при возникновении статического события.
ИЗБЕГАЙТЕ передачи null в качестве параметра аргументов события. Следует передавать EventArgs.Empty, если вы не хотите передавать какие-либо данные в метод обработки событий. Разработчики ожидают, что этот параметр не будет null.
⚠️ РАССМОТРИТЕ вызов событий, которые конечный пользователь может отменить. Это относится только к пред-событиями. Используйте System.ComponentModel.CancelEventArgs или его подкласс в качестве аргумента события, чтобы позволить конечному пользователю отменять события.

Пользовательские обработчики событий
Есть случаи, когда EventHandler<T> не может использоваться, например, когда инфраструктура должна работать с более ранними версиями CLR, которые не поддерживают обобщения. В таких случаях вам может потребоваться спроектировать и разработать пользовательский делегат обработчика событий.
ИСПОЛЬЗУЙТЕ void как тип результата для событий. Обработчик событий может вызывать несколько методов обработки событий, возможно, для нескольких объектов. Если бы методы обработки событий могли возвращать значение, для каждого вызова события было бы несколько возвращаемых значений.
ИСПОЛЬЗУЙТЕ object в качестве типа первого параметра обработчика события и называйте его sender.
ИСПОЛЬЗУЙТЕ System.EventArgs или его подкласс в качестве типа второго параметра обработчика событий и называйте его e.
ИЗБЕГАЙТЕ добавления более двух параметров в обработчик события.

Продолжение следует…

Источник: https://docs.microsoft.com/en-us/dotnet/standard/design-guidelines/
День двести двадцать седьмой. #BestPractices
Советы по разработке членов типов
Разработка полей
Принцип инкапсуляции является одним из наиболее важных понятий в объектно-ориентированной разработке. Этот принцип гласит, что данные, хранящиеся внутри объекта, должны быть доступны только этому объекту. Полезный способ интерпретации этого принципа состоит в том, что класс должен быть спроектирован так, чтобы изменения полей класса (имени или типа поля) могли быть сделаны так, чтобы не ломать никакой код, кроме кода членов самого класса. Эта интерпретация подразумевает, что все поля должны быть закрытыми. Мы исключаем константы и статические поля только для чтения из этого строгого ограничения, потому что такие поля по определению почти никогда не изменяются.

ИЗБЕГАЙТЕ предоставления открытых или защищённых полей экземпляра. Для доступа к полям нужно предоставлять свойства.
ИСПОЛЬЗУЙТЕ константные поля для констант, которые никогда не изменятся. Компилятор записывает значения полей const непосредственно в вызывающий код. Следовательно, значения const никогда не могут быть изменены без риска нарушения совместимости.
ИСПОЛЬЗУЙТЕ открытые статические поля только для чтения для предопределенных экземпляров объекта.
ИЗБЕГАЙТЕ присваивания экземпляров изменяемых типов полям только для чтения. Изменяемый тип - это тип с экземплярами, которые могут быть изменены после их создания. Например, массивы, большинство коллекций и потоков являются изменяемыми типами, но System.Int32, System.Uri и System.String являются неизменяемыми. Модификатор только для чтения в поле ссылочного типа предотвращает замену экземпляра, сохраненного в поле, но не препятствует изменению данных экземпляра поля путем вызова членов (свойств или методов), изменяющих экземпляр.

Продолжение следует…

Источник:
https://docs.microsoft.com/en-us/dotnet/standard/design-guidelines/
День двести двадцать восьмой. #BestPractices
Советы по разработке членов типов
Разработка методов расширения
Методы расширения – это функция языка, которая позволяет вызывать статические методы с использованием синтаксиса вызова метода экземпляра. Эти методы должны принимать хотя бы один параметр, представляющий экземпляр, с которым должен работать метод. Класс, который определяет такие методы расширения, называется классом-спонсором, и он должен быть объявлен как статический. Чтобы использовать методы расширения, необходимо импортировать пространство имен, определяющее класс-спонсор.

ИЗБЕГАЙТЕ беспорядочного определения методов расширения, особенно для типов, которыми вы не владеете. Если вы сами разрабатываете исходный код типа, попробуйте вместо этого использовать обычные методы экземпляра. Если вы не являетесь владельцем типа и хотите добавить метод, будьте очень осторожны. Широкое использование методов расширения может привести к загромождению API-интерфейсов типов, которые не предназначены для использования этих методов.
⚠️ РАССМОТРИТЕ использование методов расширения в следующих сценариях:
- Предоставление вспомогательной функциональности для каждой реализации интерфейса, если эта функциональность может быть объяснена в терминах основного интерфейса. Это связано с тем, что конкретные реализации не могут быть назначены интерфейсам (до С#8). Например, операторы LINQ to Objects реализованы как методы расширения для всех типов IEnumerable<T>. Таким образом, любая реализация IEnumerable<> автоматически поддерживает LINQ.
- Когда метод экземпляра вводит зависимость от некоторого типа, но такая зависимость нарушает правила управления зависимостями. Например, зависимость String от System.Uri, вероятно, нежелательна, и поэтому метод экземпляра String.ToUri(), возвращающий System.Uri, был бы неправильным дизайном с точки зрения управления зависимостями. Статический метод расширения Uri.ToUri(this string str), возвращающий System.Uri, был бы намного лучше.
ИЗБЕГАЙТЕ определения методов расширения для System.Object.
ИЗБЕГАЙТЕ помещения методов расширения в то же пространство имен, что и расширяемый тип, если это делается не для добавления методов к интерфейсам или для управления зависимостями.
⚠️ РАССМОТРИТЕ определение методов расширения в том же пространстве имен, что и расширяемый тип, если тип является интерфейсом и если методы расширения предназначены для использования в большинстве или во всех случаях.
ИЗБЕГАЙТЕ определения двух или более методов расширения с одной и той же сигнатурой, даже если они находятся в разных пространствах имен.
ИЗБЕГАЙТЕ определения методов расширения в пространствах имен, обычно связанных с другими функциями, чем та, что реализует метод расширения. Вместо этого определите их в пространстве имен, связанном с объектом, которому они принадлежат.
ИЗБЕГАЙТЕ общего именования пространств имен, выделенных для методов расширения (например, Extensions). Вместо этого используйте описательное имя (например, Routing).

Продолжение следует…

Источник:
https://docs.microsoft.com/en-us/dotnet/standard/design-guidelines/
День двести двадцать девятый. #BestPractices
Советы по разработке членов типов
Перегрузка операторов
Перегрузки операторов позволяют типам фреймворка выглядеть так, как если бы они были встроенными примитивными типами. Хотя в некоторых ситуациях это допустимо и полезно, их следует использовать с осторожностью. Существует много случаев злоупотребления, например, когда разработчики использовали перегрузку операторов для операций, которые должны быть простыми методами.

⚠️ РАССМОТРИТЕ определение перегрузки операторов в типе, который должен выглядеть как примитивный тип. Например, для System.String определены операторы == и !=.
ИЗБЕГАЙТЕ определения перегрузок операторов, за исключением типов, которые должны выглядеть как примитивные (встроенные) типы.
ИСПОЛЬЗУЙТЕ перегрузки операторов в структурах, которые представляют числа (например, System.Decimal).
ИСПОЛЬЗУЙТЕ перегрузки операторов только в тех случаях, когда очевидно, каким будет результат операции. Например, имеет смысл вычесть один DateTime из другого DateTime и получить TimeSpan. Однако нецелесообразно использовать оператор логического объединения для объединения двух запросов к базе данных или использовать оператор сдвига для записи в поток.
ИЗБЕГАЙТЕ перегрузки оператора, если хотя бы один из операндов не относится к типу, определяющему перегрузку.
ИСПОЛЬЗУЙТЕ симметричную перегрузку операторов. Например, если вы перегружаете оператор ==, вам также следует перегрузить оператор !=. Точно так же, если вы перегружаете оператор <, вы также должны перегружать оператор > и так далее.
⚠️ РАССМОТРИТЕ предоставление методов с понятными именами, которые соответствуют каждому перегруженному оператору. Многие языки не поддерживают перегрузку операторов. По этой причине рекомендуется, чтобы типы, которые перегружали операторы, включали вторичный метод с соответствующим именем, обеспечивающим эквивалентную функциональность, например, + - Add, == - Equals, ++ - Increment, & - BitwiseAnd и т.п.

Перегрузка оператора == довольно сложна. Семантика оператора должна быть совместима с несколькими другими членами, такими как Object.Equals.

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

ИЗБЕГАЙТЕ предоставления оператора преобразования, если конечные пользователи не ожидают такого преобразования.
ИЗБЕГАЙТЕ определения оператора преобразования вне домена типа. Например, Int32, Double и Decimal являются числовыми типами, а DateTime - нет. Следовательно, не должно быть оператора преобразования для преобразования Double (long) в DateTime. В таком случае более предпочтительным является конструктор.
ИЗБЕГАЙТЕ предоставления оператора неявного преобразования, если преобразование потенциально приводит к потерям. Например, не должно быть неявного преобразования из Double в Int32, потому что Double имеет более широкий диапазон, чем Int32. Явный оператор преобразования может быть предоставлен, даже если преобразование потенциально связано с потерями.
ИЗБЕГАЙТЕ выброса исключений из неявных преобразований. Конечным пользователям очень трудно понять, что происходит, потому что они могут не знать, что происходит преобразование.
ИСПОЛЬЗУЙТЕ выброс System.InvalidCastException, если вызов оператора приведения приводит к конвертации с потерями, а контракт оператора не допускает конвертации с потерями.

Продолжение следует…

Источник:
https://docs.microsoft.com/en-us/dotnet/standard/design-guidelines/
День двести тридцатый. #BestPractices
Советы по разработке членов типов
Разработка параметров. Начало
ИСПОЛЬЗУЙТЕ наименее производный тип параметра, который обеспечивает функциональность, требуемую членом. Предположим, что вы хотите разработать метод, который перечисляет коллекцию и выводит каждый элемент в консоль. Такой метод должен принимать IEnumerable в качестве параметра, а не ArrayList или IList.
ИЗБЕГАЙТЕ резервирования параметров. Если в какой-либо будущей версии требуется больше входных данных для члена, можно добавить перегруженную версию.
ИЗБЕГАЙТЕ публичных методов, которые принимают указатели, массивы указателей или многомерные массивы в качестве параметров. Указатели и многомерные массивы относительно сложно использовать должным образом. Практически во всех случаях API могут быть изменены, чтобы не принимать эти типы в качестве параметров.
НЕОБХОДИМО размещать все out параметры после всех параметров, передаваемых по значению, и всех ref параметров (исключая массивы параметров), даже если это приводит к несогласованности в порядке параметров между перегрузками. Выходные параметры можно рассматривать как дополнительные возвращаемые значения, а их группировка в конце облегчает понимание сигнатуры метода.
НЕОБХОДИМО быть последовательным в именовании параметров при переопределении членов или реализации элементов интерфейса. Это лучше передает связь между методами.

Выбор между перечислениями и булевыми параметрами
ИСПОЛЬЗУЙТЕ перечисления, если член имеет два или более булевых параметра.
ИЗБЕГАЙТЕ булевых значений, кроме случаев, когда вы абсолютно уверены, что никогда не понадобится больше двух значений. Перечисления дают вам некоторое пространство для добавления значений в будущем.
⚠️ РАССМОТРИТЕ использование булевых значений для параметров конструктора, которые действительно имеют только два состояния и просто используются для инициализации булевых свойств.

Валидация аргументов
ИСПОЛЬЗУЙТЕ валидацию аргументов открытых, защищенных или явно реализованных членов. Выбрасывайте System.ArgumentException или один из его подклассов, если валидация завершается неудачей. Обратите внимание, что фактическая проверка не обязательно должна происходить в открытом или защищенном члене. Она может выполняться на более низком в закрытом методе. Суть в том, чтобы все методы интерфейса, которые предоставляются конечным пользователям, проверяли аргументы.
ИСПОЛЬЗУЙТЕ ArgumentNullException, если в качестве аргумента передан null, а параметр не поддерживает это значение.
ИСПОЛЬЗУЙТЕ проверку параметров-перечислений. Не предполагайте, что аргументы перечисления будут в диапазоне, определенном перечислением. CLR позволяет преобразовывать любое целочисленное значение в значение перечисления, даже если это значение не определено в перечислении. При этом использование Enum.IsDefined для проверки диапазона очень неэффективно с точки зрения производительности.
❗️ ЗАМЕТЬТЕ, что изменяемые аргументы (например, ссылочные типы) могут измениться после их проверки. Если член чувствителен к безопасности, сделайте копию аргумента, а затем проверьте и обработайте его.
Передача параметров
Группы параметров по способу передачи в метод:
1. Параметры, передаваемые по значению. Член получает копию фактически переданного аргумента. Если аргумент является значимым типом, копия аргумента помещается в стек. Если аргумент является ссылочным типом, в стек помещается копия ссылки на него. Самые популярные языки CLR, такие как C#, VB.NET и C++, по умолчанию передают параметры по значению.
2. Ref параметры. Член получает ссылку на фактический переданный аргумент. Если аргумент является значимым типом, ссылка на аргумент помещается в стек. Если аргумент является ссылочным типом, в стек помещается ссылка на ссылку. Ref параметры могут использоваться, чтобы позволить члену изменять аргументы, переданные вызывающей стороной.
3. Out параметры. Аналогичны ref параметрам с некоторыми небольшими отличиями. Параметр изначально считается неопределённым и не может быть прочитан в теле члена, пока ему не присвоено какое-либо значение. Кроме того, параметру должно быть присвоено некоторое значение, прежде чем член вернёт управление.

ИЗБЕГАЙТЕ использования параметры out или ref, т.к. это требует опыта работы с указателями, понимания различий между значимыми и ссылочными типами и работы с методами с несколькими возвращаемыми значениями. Кроме того, разработчики фреймворков, проектирующие для широкой аудитории, не должны ожидать от пользователей фреймворка полного понимания различий и принципов работы с out или ref параметрами.
ИЗБЕГАЙТЕ передачи ссылочных типов по ссылке. Есть лишь несколько исключений, например, метод, который используется для обмена ссылками.

Продолжение следует…

Источник:
https://docs.microsoft.com/en-us/dotnet/standard/design-guidelines/
День двести тридцать первый. #BestPractices
Советы по разработке членов типов
Разработка параметров. Окончание
Члены с переменным числом параметров
Члены, которые могут принимать переменное число аргументов, создаются путем предоставления параметра-массива. Например, String предоставляет следующий метод:
public class String {
public static string Format(string format, object[] parameters);
}
Пользователь может вызвать метод String.Format следующим образом:
String.Format("Файл {0} не найден в {1}", new object[] { filename, directory });
Добавление ключевого слова params к параметру-массиву изменяет обеспечивает упрощённое создание временного массива:
public class String {
public static string Format(string format, params object[] parameters);
}
Это позволяет пользователю вызывать метод, передавая элементы массива непосредственно в список аргументов:
String.Format("Файл {0} не найден в {1}", filename, directory);
Обратите внимание, что ключевое слово params можно добавить только к последнему параметру.

⚠️ РАССМОТРИТЕ добавление ключевого слова params к параметру-массиву, если вы ожидаете, что конечные пользователи будут передавать массивы с небольшим количеством элементов. Если ожидается, что обычно будет передаваться много элементов, пользователи, вероятно, не будут передавать все эти элементы по одиночке, поэтому ключевое слово params не нужно.
ИЗБЕГАЙТЕ использования params, если вызывающая сторона почти всегда будет иметь данные уже в массиве. Например, члены с параметрами байтового массива почти никогда не будут вызываться путем передачи им отдельных байтов. По этой причине такие параметры в .NET Framework не используют ключевое слово params.
ИЗБЕГАЙТЕ использования params, если массив изменяется членом, принимающим параметр params. Из-за того, что многие компиляторы превращают аргументы params во временный массив, любые изменения в таком массиве будут потеряны.
⚠️ РАССМОТРИТЕ использование ключевого слова params в простой перегрузке, даже если более сложная перегрузка не может его использовать. Решите, оценят ли пользователи наличие массива params в одной перегрузке, даже если он будет не во всех перегрузках.
НЕОБХОДИМО так упорядочить параметры, чтобы можно было использовать ключевое слово params.
⚠️ РАССМОТРИТЕ использование специальных перегрузок для вызовов с небольшим количеством аргументов в чрезвычайно чувствительных к производительности API. Это позволяет избежать создания объектов массива, когда API вызывается с небольшим количеством аргументов.
❗️ ЗАМЕТЬТЕ, что null может быть передан в качестве аргумента params. Вы должны проверить, что массив не равен null перед его обработкой.

Параметры-указатели
В общем случае указатели не должны появляться в публичных API хорошо спроектированной структуры управляемого кода. Большую часть времени указатели должны быть инкапсулированы. Однако в некоторых случаях указатели требуются по причинам совместимости, и использование указателей в таких случаях целесообразно.
НЕОБХОДИМО предоставить альтернативу для любого члена, который принимает аргумент-указатель, потому что указатели не соответствуют CLS.
ИЗБЕГАЙТЕ использования дорогостоящей проверки аргументов-указателей.
НЕОБХОДИМО следовать общим соглашениям по указателям, при разработке элементов с указателями. Например, нет необходимости передавать начальный индекс, потому что простая арифметика указателя может быть использована для достижения того же результата.

Источник: https://docs.microsoft.com/en-us/dotnet/standard/design-guidelines/
День двести тридцать второй. #юмор
День двести тридцать третий. #ЗаметкиНаПолях
Форматирование строк
Статический метод Format класса string принимает составную строку формата, включающую элементы формата, а затем аргументы для замены этих элементов. Например:
string.Format("Привет, {0}.", name);
имеет один элемент формата {0} и заменяет его на значение переменной name. Каждый элемент формата в составной строке формата указывает индекс аргумента, который должен быть отформатирован, но он также может указывать следующие параметры для форматирования значения:
- Выравнивание – указывается минимальная ширина элемента. При этом выравнивание по правому краю обозначается положительным значением, а; выравнивание по левому краю - отрицательным. Указывается через запятую после индекса элемента.
- Строка формата для значения. Чаще всего используется для значений даты и времени или чисел. Например, чтобы отформатировать дату в соответствии с ISO-8601, вы можете использовать строку формата "yyyy-MM-dd". Чтобы отформатировать число в качестве значения валюты, вы можете использовать строку формата "C". Значение строки формата зависит от типа форматируемого значения и указывается после двоеточия.
Например:
decimal price = 95.25m;
string tag = string.Format("Цена: {0,9:C}", price);
Console.Write(tag);
Выведет:
Цена:    $95.25
Формат оставляет 9 символов для значения, выравнивая его по правому краю, а также добавляет символ валюты.
Для вывода в консоль отформатированной строки Console.Write можно использовать непосредственно аналогично string.Format:
Console.Write("Цена: {0,9:C}", price);

Локализация
В общих чертах, локализация - это задача убедиться, что ваш код работает правильно для всех ваших пользователей, независимо от того, где они находятся. В .Net для целей локализации применяется класс CultureInfo. Он содержит настройки культур для языка (вариации языка) или страны: летоисчисление, название дней недели и месяцев, разделители чисел, символ валюты и т.п.
Следующий код выведет в консоль список всех культур, сохранённых на данной машине, и отформатирует дату в соответствии с каждой культурой:
var cultures = CultureInfo.GetCultures(CultureTypes.AllCultures);
var birthDate = new DateTime(1986, 2, 18);
foreach (var culture in cultures)
{
string text = string.Format(
culture, "{0,-15} {1,12:d}", culture.Name, birthDate);
Console.WriteLine(text);
}
Заметьте, что в отличие от обычных случаев, когда дополнительный необязательный параметр добавляется в конец списка в перегруженной версии метода, культура в методе string.Format указывается первым значением. Это связано с тем, что последним значением указан параметр-массив для аргументов замены (см. https://t.iss.one/NetDeveloperDiary/269).
Чтобы создать объект культуры, нужно использовать метод CultureInfo.GetCultureInfo, передав в него имя культуры. Например, CultureInfo.GetCultureInfo("ru-RU") для русского языка. Кроме того, можно использовать текущую культуру, установленную на машине с помощью статического свойства CultureInfo.CurrentCulture. Это значение используется по умолчанию в методе string.Format. Однако его следует использовать с осторожностью, если предполагается, что приложение будет запускаться на разных компьютерах, т.к. культуры по умолчанию на них могут различаться. Для передачи данных между компьютерами, например, даты в формате ISO-8601 (yyyy-MM-dd), можно использовать инвариантную культуру через статическое свойство CultureInfo.InvariantCulture.

Дословные (verbatim) строки
Символ @, поставленный перед строковым литералом, сообщает конструктору строки, что нужно игнорировать символы перевода строки и символы экранирования. При этом, чтобы использовать двойные кавычки в буквальной строке, надо удвоить символ. Таким образом строку "C:\\\"Program Files\"\\IIS" можно записать как @"C:\""Program Files""\IIS".
Интерполированные строки
В C#6 форматирование строк упрощено с введением интерполированных строк. Специальный знак $ идентифицирует строковый литерал как интерполированную строку. Вместо индекса элемента формата используется выражение интерполяции. Пример выше можно переписать вот так:
string tag = $"Цена: {price,9:C}";
Замечания:
- Чтобы включить в интерполированную строку фигурные скобки, используйте повторение символа "{{" и "}}".
- Чтобы использовать тернарный условный оператор, надо заключить его в скобки: $"{name} is {age} year{(age == 1 ? "" : "s")} old."
- Буквальные интерполированные строки до C#8 обозначались как $@"...", начиная с C#8 порядок символов $ и @ значения не имеет.

Источник: Jon Skeet “C# In Depth”. 4th ed – Manning Publications Co, 2019. Глава 9.
День двести тридцать четвёртый. #ЗаметкиНаПолях
Использование nameof
С точки зрения синтаксиса, оператор nameof похож на typeof, за исключением того, что идентификатор в скобках не обязательно должен быть типом. Почему же лучше использовать nameof вместо строковых литералов? Это надёжнее. Если вы сделаете опечатку в строковом литерале, вам нечего укажет на неё, тогда как, если вы сделаете опечатку в имени операнда, вы получите ошибку во время компиляции. Кроме того, компилятор знает, что аргумент nameof связан с элементом или переменной. Если вы переименуете переменную, используя инструмент рефакторинга, имя аргумента тоже изменится. Однако заметьте, что компилятор по-прежнему не сможет определить проблему, если вы ссылаетесь на другой элемент с похожим именем.

Примеры использования
1. Валидация аргументов
При программировании по контракту каждый метод проверяет входящие данные. Это называется предусловиями. Например, далее используется статический метод класса Preconditions для проверки аргумента на null:
public void SomeMethod(string name)
{
Preconditions.CheckNotNull(name, nameof(name));

}
2. Уведомление об изменении калькулируемого свойства
Предположим, что есть класс Rectangle со свойствами Height и Width для чтения и записи и свойство только для чтения Area. Полезно иметь возможность вызвать событие для свойства Area и указать имя свойства безопасным способом:
public class Rectangle : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public double Width
{
get { … }
set
{

RaisePropertyChanged();
RaisePropertyChanged(nameof(Area));
}
}

}
3. Атрибуты
Иногда атрибуты ссылаются на другие члены, чтобы обозначить, как члены связаны друг с другом. Это используется, например, в MVC DataAnnotations, где можно с помощью атрибутов обозначать ограничения, накладываемые на модель, например, требование обязательности поля, если поставлен флажок. NUnit позволяет параметризировать тесты значениями из поля или свойства, используя атрибут TestCaseSource. В Entity Framework достаточно распространено иметь в классе два свойства: одно для внешнего ключа, а другое - для сущности, которую представляет этот ключ:
public class Employee
{
[ForeignKey(nameof(Employer))]
public Guid EmployerId { get; set; }
public Company Employer { get; set; }
}

Особенности использования nameof
1. Указание членов других типов
Стоит помнить, что nameof возвращает только имя члена, без имени класса. Например, nameof(Cultures.AllCultures) вернёт "AllCultures"
2. Обобщения
При использовании typeof, и typeof(List<string>), и typeof(List<>) допустимы и выдают разные результаты. При использовании nameof, во-первых, нельзя использовать тип без аргумента типа nameof(List<>), а во-вторых, и nameof(Action<string>) и nameof(Action<string, string>) вернут просто "Action".
Кроме того, тип параметра не выводится во время выполнения, а используется просто имя типа параметра. То есть для
static string Method<T>() => nameof(T);
И Method<Guid>() и Method<Button>() вернут просто "T".
3. Использование псевдонимов
При использовании псевдонимов nameof также выводит имя псевдонима, а не обозначенного им типа. Следующий код выведет GuidAlias, а не Guid:
using GuidAlias = System.Guid;

Console.WriteLine(nameof(GuidAlias));
4. Предопределённые псевдонимы, массивы и обнуляемые значимые типы
Оператор nameof не может быть использован с предопределёнными псевдонимами (int, char, long, и т.п.), с суффиксом ? у обнуляемых типов или с массивами. То есть все следующие варианты не будут компилироваться:
nameof(float)
nameof(Guid?)
nameof(String[])

Источник: Jon Skeet “C# In Depth”. 4th ed – Manning Publications Co, 2019. Глава 9.
👍1
День двести тридцать пятый. #ЗаметкиНаПолях
Использование using static
Директива using static служит для упрощённого импорта следующих членов классов:
1. Статические поля, свойства и методы. Канонический пример – использование класса Math:
using static System.Math;

// перевод градусов в радианы, используя Math.PI
double radians = degrees * PI / 180;
// площадь треугольника через Math.Sin
double area = 1/2 * sideA * sideB * Sin(angle);
2. Значения перечислений. Это полезно, если в коде много раз используются значения перечисления. В примере ниже перебираются значения перечисления HttpStatusCode. Добавив using static, в операторе switch можно опускать имя перечисления:
using static System.Net.HttpStatusCode;
...
switch (response.StatusCode)
{
case OK: …
case TemporaryRedirect: …
case Redirect: …
case RedirectMethod: …
case NotFound: …
default: …
}
3. Вложенные типы.
Вложенные типы чаще используются в сгенерированном коде. Однако, если вы ими пользуетесь, возможность напрямую их импортировать может упростить написание кода.

Методы расширения и using static
Два важных момента взаимодействия методов расширения и директивы using static:
1. Методы расширения из одного типа могут быть импортированы с использованием директивы using static с этим типом без импорта каких-либо методов расширения из остальной части данного пространства имен. Например, класс System.Linq.Queryable содержит методы расширения для деревьев выражений IQueryable<T>, а класс System.Linq.Enumerable содержит методы расширения для делегатов, принимающих IEnumerable<T>. Поскольку IQueryable<T> наследуется от IEnumerable<T>, используя обычную директиву using для System.Linq, вы можете использовать методы расширения, принимающие делегаты в IQueryable<T>, хотя обычно этого не требуется. Далее показано, как использование статической директивы только для System.Linq.Queryable приводит к тому, что методы расширения из System.Linq.Enumerable не подключаются:
using System.Linq.Expressions;
using static System.Linq.Queryable;
...
var query = new[] { "a", "bc", "d" }.AsQueryable();
Expression<Func<string, bool>> expr = x => x.Length > 1;
Func<string, bool> del = x => x.Length > 1;
var valid = query.Where(expr);
var invalid = query.Where(del);
Последняя строка приводит к ошибке компиляции, поскольку методу Where передаётся делегат, а не дерево выражений. Таким образом, если у вас в проекте есть файл с методами расширения для различных типов проекта, можно разделить методы расширения, относящиеся к разным типам, на разные классы расширений и импортировать каждый класс по отдельности с помощью using static.
2. Методы расширения, импортированные из типа, нельзя вызывать, как статический методы (как выше вызывался метод Sin класса Math). Вместо этого вы должны вызывать их, как если бы они были экземплярами методов расширяемого типа. Например, метод расширения Enumerable.Count:
using System.Collections.Generic;
using static System.Linq.Enumerable;

IEnumerable<string> strings = new[] { "a", "b", "c" };
int valid = strings.Count();
int invalid = Count(strings);
Последняя строка приведёт к ошибке компиляции.

Источник: Jon Skeet “C# In Depth”. 4th ed – Manning Publications Co, 2019. Глава 10.
День двести тридцать шестой. #Оффтоп
Как стать продуктивнее. Начало
В разработке ПО даже один разработчик может иметь большое значение. Вот почему найти отличных инженеров так сложно, а зарплаты программистов стремительно растут. Высокопроизводительный разработчик ПО - невероятный актив в команде. Компания готова платить им самую высокую зарплату, а конкуренция за них самая жёсткая. Эффективный высокопроизводительный разработчик - профессионал, который выполняет работу быстро и эффективно. Тот, кто обладает невероятной производительностью и высоким качеством. Кто-то, про кого вы просто не можете понять, как они могут так много делать.
Эти советы помогут значительно оптимизировать свою производительность, уменьшить стресс и быть довольным результатами.
1. Настройте мышление на высокую эффективность
Прежде всего, самое главное – захотеть стать эффективным и высокопроизводительным разработчиком. Высокопроизводительный разработчик приходит к себе на работу с намерением добиться как можно большего в этот день. Это главная директива, которую нужно помнить во время работы. Высокая эффективность приносит много пользы. С точки зрения карьеры, продуктивный разработчик - огромный актив. Вы будете получать больше прибавок, бонусов, повышений по службе и хороших отзывов. Кроме того, высокая производительность в какой-то момент превращается в игру. Выполнение заданий - это весело и просто. Еще одно преимущество - опыт работы. Если вы выполняете вдвое больше заданий в течение одного дня, вы также удваиваете свой опыт работы.
2. Пишите список целей на день
Один из наиболее эффективных инструментов повышения производительности - это простой список дел с задачами на день. Запишите в начале дня, что вы хотите сделать. Затем, по мере выполнения вычёркивайте элементы из списка. Это очень просто, но эффект потрясающий. Простое вычеркивание задач из списка дел очень радует. На самом деле это встроено в наш мозг. Когда мы чего-то добиваемся, наше тело вырабатывает дофамин, который немедленно доставляет нам удовольствие. Это, в свою очередь, заставляет нас хотеть повторить это удовольствие и сделать ещё больше работы. Другими словами - списки задач делают вас более продуктивным, а работу более приятной.

Продолжение следует...

Источник:
https://michaelscodingspot.com
День двести тридцать седьмой. #Оффтоп
Как стать продуктивнее. Продолжение
3. Планируйте подход к текущей задаче
Прежде чем писать код, отлаживать или даже касаться клавиатуры, подумайте. Как вы собираетесь решать свою задачу? Какие классы вы собираетесь создать? Где вы собираетесь устанавливать контрольные точки? С какими препятствиями вы можете столкнуться?
Вы должны задать себе все эти вопросы в самую первую очередь. Как только вы создадите план решения задачи в своей голове (или даже лучше, на бумаге), вы сможете точно спланировать свои шаги. Это приводит к организованному и эффективному рабочему процессу. Он сфокусирует вас на вашей задаче и предотвратит ненужные приключения, которые не принесут никакой пользы. В результате ваша производительность будет стремительно расти.
4. Сохраняйте спокойствие
Программирование может быть очень увлекательным, но также может быть сложным и разочаровывающим. Есть такие задачи, которые вы даже не будете знать, как начать решать. Иногда вы оцениваете, что задача будет решена за 2 часа, а в итоге решение занимает 2 дня. Или же вы можете столкнуться с жёсткими дедлайнами и переключением между задачами.
Программирование может быть действительно напряжённым, но решение простое - сохраняйте спокойствие. Злоба и разочарование затуманивают ваш разум, и в итоге вы потратите ещё больше времени на выполнение задачи или наделаете ошибок. Сохранять спокойствие легче сказать, чем сделать. У каждого свои методы для этого. Мне хватает просто помнить, что я должен быть спокоен.
5. Делайте перерывы и работайте в течение заранее определенных периодов
Есть несколько причин, почему перерывы в работе помогают повысить производительность. Для меня это сводится к двум причинам.
Когда я знаю, что собираюсь работать в течение определенного периода времени, мне легче сосредоточиться на своей задаче. Я работаю по методу «Помидора», где вы работаете 25 минут и делаете перерыв на 5 минут. Поэтому, пока я провожу 25 минут, я не чувствую желания проверить фейсбук, ответить на сообщения или переключиться на другую задачу. Я полностью сосредоточен на непосредственной задаче в эти 25 минут.
Перерывы помогают мне думать и разбирать проблемы. Вероятно, решения проблем, приходят мне в голову чаще в ванной, чем перед компьютером. Наверное, есть какая-то магия в том, чтобы встать, сделать небольшой перерыв и отвлечься на другие темы, и она работает.

Продолжение следует...

Источник:
https://michaelscodingspot.com
День двести тридцать восьмой. #Оффтоп
Как стать продуктивнее. Продолжение
6. Начинайте с самой трудной части
Во многих задачах у вас может быть какое-то технологически узкое место, которое вы не знаете, как реализовать. Всё остальное может быть простым, то, что вы делали сто раз, но вот с этим вы не знаете, как справиться.
Мой совет - начать с самого сложного. Одна из причин заключается в том, что, как только вы решите это, вы будете намного увереннее в остальном. Другая причина в том, что, работая с этой частью, вы можете понять, что задача не может быть выполнена так, как вы планировали, и вы сэкономите много времени.

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

8. Просите помощи… но не слишком часто
Никто не может отрицать, что обращение за помощью эффективно. Если вы начинаете работать в новом месте, работаете над незнакомым кодом или просто ищете совета, это чрезвычайно полезно.
Но с такой помощью есть проблема в том, что, когда кто-то что-то вам объясняет, вы понимаете меньше, чем если бы вы сами до этого додумались. Когда вы расшифруете код самостоятельно, вы получите глубокое его понимание. Все логические части сойдутся воедино, и вы сможете решить любую проблему в этом коде позже. Когда кто-то вам это объяснит, вы поймете какую-то часть уравнения, но остальное останется загадкой, пока вы в следующий раз не обратитесь за помощью.
Поэтому важно найти баланс между обращением за помощью и самостоятельным решением. С одной стороны, вы можете застрять на долгое время без посторонней помощи. С другой стороны, обращение за помощью каждый раз приведет к тому, что вы получите наименьший опыт. Мой совет - постарайтесь хоть что-нибудь выяснить самостоятельно, прежде чем просить о помощи. Таким образом, даже если вы в конечном итоге попросите о помощи, вы намного лучше поймете, что вам ответят.

Продолжение следует...

Источник:
https://michaelscodingspot.com
День двести тридцать девятый. #Оффтоп
Как стать продуктивнее. Окончание
9. Изучите суть вашей технологии
В какой бы компании вы ни работали, вы, вероятно, используете набор технологий. Это может быть язык программирования, несколько фреймворков и несколько инструментов. Если это то, чем вы постоянно пользуетесь, то стоит углубленно изучить суть вашей технологии.
Имеется в виду помимо того опыта, который вы набираете во время работы. Пройдите какой-нибудь курс, чтобы изучить технологию. Это может быть онлайн-курс, видеоуроки, книга, документация или что-то еще. Да, это займет много времени, но рассматривайте это как долгосрочное вложение.

10. Оптимизируйте себя
Чтобы стать высокопроизводительным разработчиком, вы всегда должны улучшать и оптимизировать себя. Это означает несколько вещей. С технической стороны, вы всегда должны продолжать учиться. Мы работаем в сфере, где мы можем узнавать что-то новое каждый день, и это здорово.
С другой стороны, вы должны стремиться оптимизировать свой рабочий процесс. После каждого выполненного задания вы можете спросить себя: сделал ли я это максимально эффективно? Мог бы я сделать это лучше или быстрее? Как мне снова подойти к той же проблеме? Эти вопросы являются ключом к бесконечному улучшению и повышению профессионализма.

11. Знайте, когда остановиться
Вы можете быть самым эффективным разработчиком в мире, но также важно знать, когда стоит отказаться от выполнения задачи. Обычно это задачи, в которых вы понимаете, что их выполнение займет намного больше времени, чем она заслуживает, или задачи, которые добавляют излишнюю сложность. Понятно, что желание клиента – закон, и, если клиент настаивает на какой-то функциональности, от которой вы не можете его отговорить, придётся её реализовать (или искать другого клиента). Другое дело какой-нибудь рефакторинг, который вы начали, но через неделю понимаете, что он займёт гораздо больше времени, чем предполагалось, и может сломать систему в целом ряде мест. Как разработчики, мы должны быть реалистами в таких вещах. Мы разрабатываем продукт, не стремясь к совершенству кода. Возможно, этот рефакторинг просто не стоит этого.

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

Источник: https://michaelscodingspot.com
День двести сороковой. #юмор
Как выглядят избыточные комментарии в коде.
День двести сорок первый. #ЗаметкиНаПолях
Tracepoints: отладка без помех. Начало
Вы используете отладочный вывод? Давайте будем честными, мы все когда-либо делали это. Будь то Debug.WriteLine(), console.log(), print() и т. д. Вывод данных в консоль - обычная практика. А вы когда-нибудь забывали удалить отладочный вывод из живого кода? И вдруг то, что кажется простым и приятным подходом к отладке, превращается в большую работу по очистке кода. Кроме того, если отладочный вывод используется в нескольких местах, его периодически приходится удалять, чтобы не загромождать консоль.
Если это так, то Tracepoints (точки трассировки) - отличный инструмент, который вы можете использовать в Visual Studio. Эта функция позволяет регистрировать нужную информацию без изменения кода и инициализируется аналогично точкам останова. Когда вы закончите отладку, просто нажмите на точку трассировки, чтобы удалить ее.
Допустим, мы хотим посмотреть значение counter для каждой итерации цикла for (см. картинку ниже). Одним из решений является использование оператора отладки, такого как Debug.WriteLine(counter). В то время как это, безусловно, решает эту простую задачу, нам потребовалось изменить наш код а позже потребуется удалить эту строку, чтобы она не попала в продакшн.
С помощью точек трассировки вы можете делать это, не изменяя код. Обратите внимание, что, когда вы добавляете сообщение в поле «Show a message in the Output window field» в меню Actions (см. картинку), вы никак не изменяете свой исходный код. Это позволяет вам получать необходимую информацию в окне вывода Visual Studio, без ущерба для читабельности вашего кода. Кроме того, когда вы закончите отладку, просто нажмите на точку трассировки один раз, чтобы удалить её. Но даже если вы забыли это сделать, не стоит беспокоиться о постороннем выводе, потому что точки трассировки существуют только локально на вашем компьютере.

Продолжение следует…

Источник:
https://devblogs.microsoft.com/visualstudio/tracepoints/
День двести сорок второй. #ЗаметкиНаПолях
Tracepoints: отладка без помех. Окончание
Допустим, мы хотим выводить только чётные значения счётчика или значения только во время определённой итерации цикла. В блоке Conditions мы можем добавлять условия аналогично условным точкам останова.
Есть три типа условий:
1. Условное выражение (Conditional Expression): сообщение отображается только при определенных условиях, например, counter == 5.
2. Счетчик выполнений (Hit Count): это условие позволяет вам выводить данные только после того, как эта строка кода выполнится заданное количество раз.
3. Фильтр (Filter): точка трассировки будет активирована только на указанных устройствах, процессах или потоках.
Добавление этих условий не изменяет ваш исходный код и, в отличие от точек останова, не останавливает программу и не потребует от вас многократного перехода из программы в отладку и обратно (если установлен флажок «Continue code» в блоке Actions).

Советы
1. Сообщения трассировки отправляются в окно Output. Их легко потерять среди множества других вещей, которые отправляются в это же окно. Если щелкнуть правой кнопкой мыши в окне Output, вы можете отключать типы сообщений, такие как сообщения об исключениях, сообщения о выходе из процесса и т. д. Так вам будет легче сосредоточиться на выводе вашей точки трассировки.
2. Если ваша текущая задача требует наличия всех сообщений, ещё один способ облегчить поиск выходных данных - это поставить уникальный префикс сообщения перед выводом данных, например, MyOutput: {counter}. При отладке вы можете использовать команду поиска Ctrl+F в окне Output, чтобы найти префикс, который вы установили, и окно отобразит для вас нужный вывод.
3. Чтобы временно отключить точку трассировки без её удаления, щёлкните по ней, удерживая Shift.
4. Для одновременного просмотра, отключения или удаления всех точек трассировки и точек останова в текущем файле перейдите в Debug -> Windows -> Breakpoints, чтобы открыть окно точек останова.

В некоторых случаях оператор отладки, такой как Debug.WriteLine(), может быть предпочтительнее точек трассировки. Например, если вы хотите всегда видеть какой-либо вывод в отладчике, который сохраняется за пределами текущего сеанса отладки. Кроме того, точки трассировки менее эффективны. И наконец, точки трассировки имеют ограничения в отношении того, какие данные они могут собирать, поскольку они могут только виртуально оценивать значения функций. Несмотря на некоторые из этих ограничений, точки трассировки являются отличным инструментом в вашем наборе средств отладки.

Дополнительная информация про точки трассировки находится в документации по Visual Studio: https://docs.microsoft.com/en-us/visualstudio/debugger/using-tracepoints?view=vs-2019

Источник: https://devblogs.microsoft.com/visualstudio/tracepoints/