.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
День двести шестьдесят восьмой. #ЗаметкиНаПолях
Нетривиальный пример использования явной реализации интерфейса
Про то, что такое явная реализация интерфейса, я писал достаточно давно. Однако на днях попался интересный пример использования, который захотелось записать на будущее.
Когда класс реализует интерфейс, он обязан содержать методы с сигнатурами, которые соответствуют указанным в интерфейсе. Вы можете использовать явную реализацию интерфейса, чтобы сделать методы, реализующие интерфейс, видимыми только тогда, когда к объекту обращаются через интерфейсную ссылку.
Например, вы можете создать интерфейс IPrintable, который определяет методы, используемые для печати любого объекта. Это хорошая идея, потому что теперь у принтера можно попросить напечатать любой объект класса, реализующего IPrintable. Если подумать, то методы интерфейса IPrintable имеют смысл только тогда, когда они используются чем-то, пытающимся напечатать объект. Не имеет смысла вызывать методы печати в каком-либо ином контексте, кроме как через ссылку на IPrintable. Этого можно добиться, сделав реализацию методов печати явной, то есть добавив имя интерфейса к объявлению тела метода.
Следующий код показывает класс Report, который реализует интерфейс IPrintable. Класс Report содержит два метода: GetPrintableText и GetTitle, которые объявлены в интерфейсе IPrintable.
interface IPrintable
{
string GetPrintableText(int page);
string GetTitle();
}
class Report : IPrintable
{
string IPrintable.GetPrintableText(int page)
{
return "Текст отчёта";
}
string IPrintable.GetTitle()
{
return "Заголовок отчёта";
}
}
Теперь единственный способ получить доступ к этим методам в экземпляре отчёта – приведя отчёт к IPrintable. На рисунке ниже показан эффект в Visual Studio: методы GetPrintableText и GetTitle скрыты от IntelliSense, если обращаться через класс Report напрямую, но при обращении через IPrintable они видны.
Таким образом можно спрятать методы, не относящиеся непосредственно к функционалу класса. Потребители класса-отчёта (кроме принтера) не смогут использовать эти методы, т.к. они им не нужны. Более того, используя частичные (partial) классы, можно и сам код этих методов вынести в отдельный файл, чтобы он не загромождал код основного функционала.

Источник: Rob Miles “Exam Ref 70-483 Programming in C#”. 2nd ed - Pearson Education, Inc., 2019. Глава 2.
День двести шестьдесят девятый. #BestPractices
Разработка Исключений
Выброс Исключений
Ошибка выполнения возникает всякий раз, когда член класса не может сделать то, для чего он был предназначен. Например, если метод OpenFile не может вернуть дескриптор открытого файла вызывающей стороне, это будет считаться ошибкой выполнения. Большинству разработчиков удобно использовать исключения для ошибок использования, таких как деление на ноль или нулевые ссылки. В .Net Framework исключения используются для всех ошибок, включая ошибки выполнения.
ИЗБЕГАЙТЕ возвращения кодов ошибок из методов.
ИСПОЛЬЗУЙТЕ исключения для сообщения об ошибках выполнения. Исключения являются основным средством сообщения об ошибках в фреймворках.
⚠️ РАССМОТРИТЕ вариант завершение процесса, вызвав System.Environment.FailFast вместо выброса исключения, если ваш код сталкивается с ситуацией, когда он небезопасен для дальнейшего выполнения.
ИЗБЕГАЙТЕ использования исключений для контроля нормального потока выполнения.

За исключением сбоев системы и операций, в которых возможно возникновение состояния гонки, разработчики фреймворков должны создавать такой API, чтобы пользователи могли писать код, который не генерирует исключения. Например, вы можете предоставить способ проверки предварительных условий перед вызовом метода. Метод, используемый для проверки предварительных условий другого метода, часто называют тестером, а метод, фактически выполняющий работу, называют исполнителем.

⚠️ РАССМОТРИТЕ последствия для производительности при использовании исключений. Частый выброс исключений может заметно повлиять на производительность большинства приложений.
❗️ НЕОБХОДИМО задокументировать все исключения, которые могут выбрасывать открытые члены вследствие нарушения контракта кода (а не из-за сбоя системы), и рассматривать их как часть контракта. Исключения, являющиеся частью контракта, не должны изменяться от одной версии к другой (то есть не должен изменяться тип исключения, и не должны добавляться новые исключения).
ИЗБЕГАЙТЕ создания открытых членов, выбрасывающих или не выбрасывающих исключение в зависимости от какой-либо настройки.
ИЗБЕГАЙТЕ открытых членов, возвращающих исключение, либо задающих исключение out параметру. Возврат исключений из общедоступных API вместо их выбрасывания сводит на нет многие преимущества системы обработки исключений.
⚠️ РАССМОТРИТЕ использование методов-конструкторов исключений. Обычно из разных мест кода выбрасывается одно и то же исключение. Чтобы избежать раздувания кода, используйте вспомогательные методы, которые создают исключение и инициализируют его свойства. Кроме того, члены, которые генерируют исключения, не встраиваются компилятором. Перемещение оператора throw внутрь вспомогательного метода может позволить встроить исходный член.
ИЗБЕГАЙТЕ выбрасывания исключений из блоков фильтрации исключений. Когда фильтр исключений выбрасывает новое исключение, оно перехватывается CLR, а фильтр возвращает false. Это поведение неотличимо от фильтра, который успешно выполняется и возвращает false, поэтому такое исключение очень трудно отладить.
ИЗБЕГАЙТЕ явного выброса исключения из блоков finally. Но из блока finally допустимо вызывать методы, которые могут выбрасывать исключения.

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

Exception и SystemException
ИЗБЕГАЙТЕ выбрасывания System.Exception или System.SystemException.
ИЗБЕГАЙТЕ перехватывания System.Exception или System.SystemException во внутреннем коде, если только вы не собираетесь их повторно выбросить.
ИЗБЕГАЙТЕ перехватывания System.Exception или System.SystemException, за исключением обработчиков исключений верхнего уровня.

ApplicationException
ИЗБЕГАЙТЕ выбрасывания и наследования от ApplicationException.

InvalidOperationException
ИСПОЛЬЗУЙТЕ InvalidOperationException, если объект находится в неподходящем состоянии.

ArgumentException, ArgumentNullException и ArgumentOutOfRangeException
ИСПОЛЬЗУЙТЕ ArgumentException или один из его подтипов, если члену передаётся неверный аргумент. Отдавайте предпочтение наиболее производному типу исключения, если это возможно.
ИСПОЛЬЗУЙТЕ свойство ParamName при создании одного из подклассов ArgumentException. Это свойство представляет имя параметра, вызвавшего исключение. Обратите внимание, что это свойство также может быть установлено с использованием одной из перегрузок конструктора.
ИСПОЛЬЗУЙТЕ ключевое слово value для имени неявного параметра-значения в установщике свойства.

NullReferenceException, IndexOutOfRangeException и AccessViolationException
ИЗБЕГАЙТЕ явного или неявного выбрасывания исключений типа NullReferenceException, AccessViolationException или IndexOutOfRangeException в публично вызываемом API. Эти типы исключения зарезервированы, выбрасываются механизмом выполнения и в большинстве случаев указывают на ошибку в коде. Выполняйте проверку аргументов, чтобы избежать выброса этих исключений. Выброс этих исключений раскрывает подробности реализации вашего метода, которая может меняться со временем.

StackOverflowException
ИЗБЕГАЙТЕ явного выбрасывания StackOverflowException. Это исключение должно явно выбрасываться только CLR.
ИЗБЕГАЙТЕ перехвата StackOverflowException. Практически невозможно написать согласованный управляемый код при наличии произвольных переполнений стека. Неуправляемые части CLR остаются согласованными благодаря использованию проверок для перемещения переполнений стека в четко определенные места, а не путем восстановлений после произвольных переполнений стека.

OutOfMemoryException
ИЗБЕГАЙТЕ явного выбрасывания OutOfMemoryException. Это исключение должно выбрасываться только инфраструктурой CLR.

ComException, SEHException и ExecutionEngineException
ИЗБЕГАЙТЕ явного выбрасывания COMException, ExecutionEngineException или SEHException. Эти исключения должны выбрасываться только инфраструктурой CLR.

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

Паттерн Тестер-Исполнитель (Tester-Doer)
Иногда производительность элемента, генерирующего исключение, можно улучшить, разбив его на две части. Рассмотрим на метод Add интерфейса ICollection<T>:
ICollection<int> numbers = …
numbers.Add(1);
Метод Add выбрасывает исключение, если коллекция доступна только для чтения. Это может быть проблемой производительности в сценариях, где ожидается, что вызов метода, часто будет заканчиваться неудачей. Один из способов сгладить проблему - проверять, доступна ли коллекция для записи, прежде чем добавлять значение:
ICollection<int> numbers = …

if (!numbers.IsReadOnly)
numbers.Add(1);
Член класса, используемый для проверки условия (в нашем примере свойство IsReadOnly), называется тестером. Член, используемый для выполнения операции, потенциально приводящей к исключению (в нашем примере метод Add), называется исполнителем.

⚠️ РАССМОТРИТЕ использование паттерна Tester-Doer для членов класса, которые могут генерировать исключения в распространённых сценариях, чтобы избежать проблем производительности, связанных с исключениями.

Паттерн Try-Parse
Для API, чрезвычайно чувствительных к производительности, можно использовать еще более быстрый паттерн Try-Parse. Он требует изменения имени члена, чтобы оно отражало суть того, что он будет делать. Например, DateTime определяет метод Parse, который выбрасывает исключение в случае сбоя парсинга строки. И метод TryParse, который пытается выполнить парсинг, но возвращает false, если он завешается неудачей, а в случае успеха возвращает результат в out параметре.
public struct DateTime
{
public static DateTime Parse(string dateTime) { … }
public static bool TryParse(string dateTime, out DateTime result) { … }
}
При использовании этого паттерна важно строго определить функциональность блока try. Если в методе произойдёт сбой по какой-либо иной причине, кроме определённой в блоке try, метод всё равно должен выбросить соответствующее исключение.

⚠️ РАССМОТРИТЕ использование паттерна Try-Parse для членов, которые могут генерировать исключения в распространённых сценариях, чтобы избежать проблем производительности, связанных с исключениями.
ИСПОЛЬЗУЙТЕ префикс «Try» и логический тип возвращаемого значения для методов, реализующих этот паттерн.
❗️ НЕОБХОДИМО добавить и обычный метод, генерирующий исключение, для каждого метода, использующего паттерн Try-Parse.

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

Красота стиля, гармония, грация и хороший ритм зависят от простоты.
- Платон

В одном предложении собраны ценности, к которым мы, разработчики программного обеспечения, должны стремиться:
- Читаемость
- Сопровождаемость
- Скорость разработки
- Красота
Платон говорит нам, что фактором, способствующим всем этим качествам, является простота.
Что такое красивый код? Это потенциально очень субъективный вопрос. Восприятие красоты в значительной степени зависит от жизненного опыта, так же, как и любое наше восприятие чего-либо. Люди, получившие образование в области искусства, воспринимают красоту (или, по крайней мере, подходят к ней) иначе, чем технари. Специалисты по искусству склонны подходить к красоте в программном обеспечении, сравнивая программное обеспечение с произведениями искусства, в то время как технари склонны говорить о симметрии и золотом сечении, пытаясь привести вещи к формулам. По моему опыту простота является основой большинства аргументов с обеих сторон.
Подумайте об исходном коде, который вы изучали. Если вы не потратили время на изучение кода других людей, прекратите читать это прямо сейчас и найдите для изучения какой-нибудь открытый исходный код. Шутки в сторону! Я серьезно! Найдите в Интернете какой-нибудь код на выбранном вами языке, написанный известным, признанным экспертом.
Код, который я считаю красивым, имеет ряд общих свойств. Главным среди них является простота. Я считаю, что независимо от того, насколько сложным является приложение или система вообще, отдельные части должны быть простыми: простые объекты, решающие единственную задачу, содержащие такие же простые, чёткие методы с описательными именами. Некоторые люди думают, что идея иметь короткие методы из 5–10 строк кода является экстремальной, а в некоторых языках этого довольно тяжело достичь. Но я всё равно считаю, что такая краткость является желанной целью.
Суть в том, что красивый код - это простой код. Каждая отдельная часть остается простой с простыми обязанностями и простыми связями с другими частями системы. Таким образом, мы можем поддерживать наши системы с течением времени с помощью чистого, простого, тестируемого кода, обеспечивая высокую скорость разработки в течение всей жизни проекта.
Красота рождается и живёт в простоте.

Источник: https://www.oreilly.com/library/view/97-things-every/9780596809515/
День двести семьдесят третий. #юмор
Работает - не трогай!
День двести семьдесят четвёртый. #ЗаметкиНаПолях
Сопоставление с шаблоном. Начало
Основная идея шаблона - проверить определенный аспект значения и использовать результат этого теста для выполнения другого действия. Да, это похоже на if, но шаблоны, как правило, используются либо для расширения возможностей условия, либо для расширения возможностей внутри самого действия, выбранного на основе шаблона. Ещё раз, сопоставление с шаблоном не позволяет вам сделать что-то, что вы не могли сделать раньше; оно позволяет вам выразить то же самое намерение более чётко.
Предположим, у вас есть абстрактный класс Shape, который определяет абстрактное свойство Area и производные классы Rectangle, Circle и Triangle. К сожалению, для вашего текущего приложения вам не нужна площадь фигуры; вам нужен её периметр. Допустим, вы не можете изменить классы, добавив в них свойство Perimeter, но вы знаете, как его вычислить для всех интересующих вас классов. До C#7 метод Perimeter мог бы выглядеть примерно так:
static double Perimeter(Shape shape)
{
if (shape == null)
throw new ArgumentNullException(nameof(shape));

Rectangle rect = shape as Rectangle;
if (rect != null)
return 2 * (rect.Height + rect.Width);

Circle circle = shape as Circle;
if (circle != null)
return 2 * PI * circle.Radius;

Triangle triangle = shape as Triangle;
if (triangle != null)
return triangle.SideA + triangle.SideB + triangle.SideC;

throw new ArgumentException($"Периметр типа {shape.GetType()} неизвестен", nameof(shape));
}
Это некрасиво, слишком длинно и слишком много повторяющегося кода. Один и тот же шаблон «проверить, является ли фигура определённым типом, а затем использовать свойства этого типа», встречается три раза. Важно отметить, что, хотя здесь есть несколько операторов if, тело каждого из них возвращает значение, поэтому вы всегда выбираете только один вариант выполнения. Тот же код может быть написан на C#7 с использованием шаблонов в операторе switch:
static double Perimeter(Shape shape)
{
switch (shape)
{
case null:
throw new ArgumentNullException(nameof(shape));
case Rectangle rect:
return 2 * (rect.Height + rect.Width);
case Circle circle:
return 2 * PI * circle.Radius;
case Triangle tri:
return tri.SideA + tri.SideB + tri.SideC;
default:
throw new ArgumentException(...);
}
}
Здесь вас иногда интересует только значение (для случая null), а иногда интересует тип значения (прямоугольник, круг или треугольник). При сопоставлении по типу также вводится новая переменная этого типа, которая используется для вычисления периметра. В C#7.0 представлены три вида шаблонов: констант, типов и шаблон var. О них далее.

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

Источник: Источник: Jon Skeet “C# In Depth”. 4th ed – Manning Publications Co, 2019. Глава 12.
День двести семьдесят пятый. #ЗаметкиНаПолях
Сопоставление с шаблоном. Продолжение
В C#7 представлены три вида шаблонов: шаблоны констант, шаблоны типов и шаблон var. Использовать сопоставление с шаблоном можно в двух контекстах: в операторе is и операторе switch. Каждый шаблон пытается соответствовать входным данных. Это может быть любое выражение, не содержащее указателей.

Шаблоны констант
Шаблон констант соответствует своему названию: он представляет собой константное выражение времени компиляции, которое затем проверяется на равенство с входными данными. Если и входные данные, и константа являются целочисленными выражениями, они сравниваются с помощью ==. В противном случае вызывается статический метод object.Equals, который позволяет безопасно проверять на null. Например:
static void Match(object input)
{
if (input is "hello")
Console.WriteLine("Это строка hello");
else if (input is 10)
Console.WriteLine("Это значение int 10");
else
Console.WriteLine("Совпадений не найдено");
}
static void Main()
{
Match("hello");
Match(10);
Match(10L);
}
Вывод в основном понятен, кроме последней строки:
Это строка hello
Это значение int 10
Совпадений не найдено
Если целые числа сравниваются с использованием ==, почему последний вызов Match(10L) не находит совпадений? Дело в том, что тип ввода во время компиляции не является целочисленным типом, это объект, поэтому компилятор генерирует код, эквивалентный вызову object.Equals(x, 10). А он возвращает false, т.к. значение x представляет собой упакованное Int64, а не упакованное Int32.

Шаблоны типов
Шаблон типа состоит из типа и идентификатора, похожего на объявление переменной. Шаблон соответствует, если входной параметр является значением этого типа, что похоже на обычный оператор is. Преимущество использования шаблона в том, что он также вводит новую переменную шаблона этого типа, инициализируемую значением, если шаблон соответствует. Если шаблон не совпадёт, переменная всё равно будет существовать, но не будет определена. Если на входе null, он не будет соответствовать ни одному типу. Также можно использовать пустую переменную _, тогда переменная не будет создана. Пример использования шаблона типа был в предыдущем посте.
Шаблон типа обычно используется для замены либо комбинации as/if, либо, блока if с приведением типов. Тип, указанный в шаблоне типа, не может быть обнуляемым значимым типом. То есть нельзя написать:
if (value is int? t) 
...
Но можно использовать параметр типа, который в свою очередь может быть обнуляемым значимым типом:
static void CheckType<T>(object value)
{
if (value is T t)
Console.WriteLine(t);
}
CheckType<int?>(null);
CheckType<int?>(5);
В этом случае шаблон найдёт соответствие только тогда, когда значение не равно null.

Шаблон var
Шаблон var выглядит как шаблон типа, но в качестве типа используется var:
someExpression is var x
Как и шаблоны типов, он вводит новую переменную, но ничего не проверяет. Он всегда совпадает (даже с null), в результате чего получается новая переменная с тем же типом и значением, что и у входного параметра. Поэтому этот шаблон бесполезен в операторе is, а используется в операторе switch в качестве последнего варианта, когда все остальные шаблоны не совпали.

В C#8 введены возможности сопоставления с шаблоном ещё более расширены.

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

Источник: Источник: Jon Skeet “C# In Depth”. 4th ed – Manning Publications Co, 2019. Глава 12.
День двести семьдесят шестой. #ЗаметкиНаПолях
Сопоставление с шаблоном. Окончание
Охранные выражения
Помимо простого сравнения с шаблоном в блоке case вы можете использовать новый фрагмент синтаксиса – охранное выражение после ключевого слова when:
case pattern when expression:
Выражение должно соответствовать булеву значению, так же как условие оператора if. Тело case будет выполнено, только если выражение истинно. Выражение может использовать шаблоны, тем самым вводя дополнительные переменные шаблона. Например:
switch (obj)
{
case Shape shape when shape.Area == 0:

break;
case Rectangle rect when rect.Area > 0:

break;
case Circle circle when circle.Area > 0:

break;
case Shape shape:

break;
case null:

break;
default:

break;
}
Когда вы используете охранные выражения, вполне возможно использовать один и тот же шаблон несколько раз, потому что при первом совпадении с шаблоном охранное выражение может вернуть false.

Область видимости
Областью видимости переменной шаблона является блок кода, включающий выражение. У этого есть как преимущества, так и недостатки. Недостатком является то, что переменная шаблона может быть не определена, если совпадения с шаблоном не произойдёт. То есть использовать переменную text внутри блока if безопасно, а после - нет:
if (input is string text)
{
Console.WriteLine(text);
}
С другой стороны, переменная определяется сразу же в шаблоне, таким образом можно использовать следующую конструкцию:
if (input is int x && x > 100)
{
Console.WriteLine($"Это большое целое число: {x}");
}
Вы можете использовать x после &&, потому что это выражение оценивается, только если первый операнд оценивается как истина. Можно использовать x внутри оператора if, потому что оно выполняется, только если оба операнда истинны.
Если переменная объявляется непосредственно в теле case, область действия этой переменной - весь оператор switch, включая другие блоки case. Но это не включает переменные, объявленные в самих блоках case. Область действия этих переменных - только тело соответствующего блока case.

Порядок оценки операторов switch при сопоставлении с шаблоном
Почти во всех ситуациях порядок блоков case при сравнении с константами не имеет значения, потому что каждый блок соответствует одному постоянному значению, а используемые константы должны быть разными. С шаблонами это не так. Порядок оценки оператора switch на основе шаблона можно описать так:
- Каждое условие оценивается в порядке следования в коде.
- Тело блока default выполняется только после того, как все остальные условия были проверены, независимо от того, где оно расположено в операторе switch.
Тут на помощь приходит компилятор: случай без охранного выражения всегда выполнится, если шаблон типа совпадёт. Компилятор знает об этом, поэтому, если вы поместите выше случаи, «скрывающие» те, что следуют за ними, компилятор сообщит об ошибке.

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

Источник: https://www.oreilly.com/library/view/97-things-every/9780596809515/
День двести семьдесят восьмой. #BestPractices
Советы по использованию общих типов
Атрибуты
System.Attribute - это базовый класс, используемый для определения пользовательских атрибутов.
Атрибуты - это аннотации, которые можно добавлять к программным элементам (сборкам, типам, членам и параметрам). Атрибуты могут иметь одно или несколько свойств, которые содержат дополнительные данные, связанные с атрибутом. Некоторые свойства атрибута должны быть указаны при применении атрибута. Они называются обязательными свойствами или обязательными аргументами, и представлены как позиционные параметры конструктора. Свойства, которые не обязательно указывать при применении атрибута, называются необязательными. Они задаются именованными параметрами конструктора.

ИСПОЛЬЗУЙТЕ суффикс Attribute в именах классов пользовательских атрибутов.
❗️ НЕОБХОДИМО применять атрибут AttributeUsageAttribute к классам пользовательских атрибутов.
ИСПОЛЬЗУЙТЕ изменяемые свойства для необязательных аргументов атрибута.
ИСПОЛЬЗУЙТЕ неизменяемые (только get) свойства для обязательных аргументов атрибута.
ИСПОЛЬЗУЙТЕ параметры конструктора для инициализации свойств, соответствующих обязательным аргументам. Каждый параметр должен иметь то же имя (отличающееся регистром), что и соответствующее свойство.
ИЗБЕГАЙТЕ добавления параметров конструктора для инициализации свойств, соответствующих необязательным аргументам. Другими словами, не создавайте в классе атрибута свойств, которые могут быть установлены как с помощью конструктора, так и с помощью аксессора. Чётко определите, какие аргументы являются обязательными, а какие нет, и избегайте наличия двух способов сделать одно и то же.
ИЗБЕГАЙТЕ перегрузки пользовательских конструкторов атрибутов. Наличие только одного конструктора четко сообщает пользователю атрибута, какие аргументы являются обязательными, а какие - необязательными.
ИСПОЛЬЗУЙТЕ закрытые (sealed) классы для атрибутов, если это возможно. Это ускоряет поиск атрибута.

Подробнее об атрибутах здесь, здесь и здесь.

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

ИЗБЕГАЙТЕ использования слабо типизированных коллекций в общедоступных API. Тип всех возвращаемых значений и параметров, представляющих элементы коллекции, должен быть точным типом элемента, а не каким-либо из его базовых типов (это относится только к открытым членам коллекции).
ИЗБЕГАЙТЕ использования ArrayList или List<T> в открытых API. Эти типы являются структурами данных, предназначенными для использования во внутренней реализации, а не в публичных API. List<T> оптимизирован для производительности за счет гибкости и чистоты API. Например, если вы вернёте List<T>, вы никогда не сможете получать уведомления, когда код клиента изменяет коллекцию. Кроме того, List<T> предоставляет множество членов, таких как BinarySearch, которые бесполезны или неприменимы во многих сценариях.
ИЗБЕГАЙТЕ использования Hashtable или Dictionary<TKey, TValue> в общедоступных API. Они предназначенными для использования во внутренней реализации. Публичные API должны использовать IDictionary, IDictionary<TKey, TValue> или пользовательский тип, реализующий один или оба этих интерфейса.
ИЗБЕГАЙТЕ использования IEnumerator<T>, IEnumerator или любого типа, реализующего любой из этих интерфейсов, кроме для типа возврата метода GetEnumerator. Типы, возвращающие итераторы из методов, отличных от GetEnumerator, нельзя использовать с оператором foreach.
ИЗБЕГАЙТЕ использования обоих интерфейсов IEnumerator<T> и IEnumerable<T> для одного типа. То же относится и к необобщённым интерфейсам IEnumerator и IEnumerable.

Параметры коллекции
ИСПОЛЬЗУЙТЕ наименее специализированный возможный тип в качестве типа параметра. Большинство членов, принимающих коллекции в качестве параметров, используют интерфейс IEnumerable<T>.
ИЗБЕГАЙТЕ использования ICollection<T> или ICollection в качестве параметра просто для доступа к свойству Count. Вместо этого рассмотрите возможность использования IEnumerable<T> или IEnumerable и динамической проверки, реализует ли объект ICollection<T> или ICollection.

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

Источник:
https://docs.microsoft.com/en-us/dotnet/standard/design-guidelines/
День двести восемьдесят первый. #оффтоп #dotnext
В Москве проходит конференция DotNext. Топовые докладчики и конские цены за билеты (даже просто за видео всех докладов). Но есть бесплатная трансляция из одного из залов. Вот запись вчерашнего дня. А на канале конференции есть куча видео выступлений с прошедших конференций. Эх, где бы найти недельку свободного времени.
День двести восемьдесят второй.
Сертификат Microsoft. Шаг предпоследний
Завтра день Хэ. Записался на экзамен на 10 утра. За сегодня нужно очистить рабочий стол от... в общем от всего: дополнительные мониторы, девайсы, ручки, карандаши, книжки, листочки, часы и всё остальное в пределах доступности. Кроме того, нужно провести тест системы на скорость интернета, качество камеры и микрофона, сфотографировать себя, паспорт и пустой стол. Завтра помимо этого придётся забарикадироваться, чтобы в комнату никто не входил и не издавал громких звуков, которые наблюдающий мог бы расценить как подсказку. Да, специально обученный человек будет через вебкамеру наблюдать за мной на всём протяжении экзамена. Такие дела.
А пока вот ещё один ресурс... нет, всё-таки не для подготовки, а для того, чтобы освежить память перед экзаменом (хотя там 8 часов видео). Ребята с шутками-прибаутками обсуждают все основные моменты, так что смотрится легко. Однако видео довольно старые, поэтому частично там уже давно устаревшая информация, например, про проекты под Windows 8, но в принципе, если совсем уж нечего делать, можно посмотреть (как обычно, на английском, конечно).
https://channel9.msdn.com/Series/Programming-in-CSharp-Jump-Start
День двести восемьдесят третий.
Сертификат Microsoft. Экзамен 70-483
УХ!!!! Это было волнительно!!!
Перед экзаменом нужно пройти тест системы. Чтобы работал интернет, микрофон, камера, направленная на тебя. Далее сфоткать себя, паспорт и рабочий стол со всех сторон. После этого к тебе подключается наблюдатель, проверяет твои данные и просит ещё раз показать комнату вокруг и рабочий стол (чтобы никого и ничего не было). Также закрыть все сторонние приложения, кроме специального, в котором будешь сдавать тест. Потом нужно прочитать (да, это стоит прочитать) и согласиться с договором о неразглашении. Ты соглашаешься, что не будешь раскрывать информацию о вопросах, не попытаешься никаким образом хакнуть систему, скачать вопросы или ещё как-нибудь сжульничать. При любом подозрении Microsoft вправе аннулировать результаты экзамена и запретить сдавать экзамены впредь. Так что о деталях вопросов не расскажу, извините.
После этого даётся опрос, не относящийся собственно к тесту (типа социология): сколько лет кодишь, сколько в C#, и (самое интересное!) как ты оцениваешь уровень своих знаний в каждой области, по которой проводится тест. Ответил честно, что тему «Отладка приложений и реализация функций безопасности» знаю плоховато. Написано было, что это не повлияет ни на вопросы, ни на результаты теста. Уж не знаю, действительно ли это так, но показалось, что именно этой темой меня больше всего в тесте и задалбывали 😊 Хотя, может быть, просто показалось.
Что касается теста. На всё даётся 2 часа времени. У меня был 41 вопрос (не знаю, возможно, количество варьируется). Вопросы действительно охватывают все перечисленные в описании экзамена темы. Кстати, насколько уж я скептически относился к предложенной в описании экзамена книге по подготовке и видеокурсу по экзамену, но пренебрегать ими точно не стоит. На несколько вопросов ответил вот ровно оттуда. Особенно это касается тем, которые редко использую в своей практике: шифрование, запись в журнал событий Windows, сериализация (я использую широко распространённый пакет Newtonsoft.Json, а там вопросы по родным майкрософтовским классам).
Во многих статьях, которые я приводил ранее, советуют отмечать вопросы для перепроверки позже. Так вот, обязательно делайте это! Если вы не отметили вопрос, больше к нему вернуться будет нельзя. Я отмечал все и перепроверял. В одном действительно нашёл ошибку из-за своей невнимательности.
Вопросы хитрые, но не то, чтобы слишком. Авторы отказались от множественных вариантов и вариантов, где ни один ответ не является верным. Думаю, это правильно. Там, где надо выбрать несколько вариантов, говорится, что их ровно 2 или ровно 3. Соответственно, можно даже логически домыслить, что вот этот ответ верный, но он не согласуется с остальными.
Что в итоге? Нужно набрать 700 баллов, причём никто не говорит, сколько стоит каждый вопрос. Я набрал 828!!! 🥳 После экзамена показывается шкала с результатами в каждой области знаний, у меня 100% в «Создании и использовании типов», в остальных поменьше (от 75 до 90). Правильных ответов или указаний, в каких вопросах ошибки, не дают. Возможно, подробная информация будет позже на сайте Microsoft (им нужно до 7 рабочих дней, чтобы обработать результат и выдать сертификат). Как только будет готово, поделюсь здесь.
Как-то так.

PS: Это вовсе не значит, что цель достигнута, «всем спасибо, все свободны»))) Я продолжу выпускать заметки и мысли на канале. А поскольку по работе занят веб-разработкой, то следующая цель – сертификат по ASP.NET MVC.

Спасибо, что читаете.
Пока вот чего дали)))
День двести восемьдесят четвёртый. #BestPractices
Советы по использованию общих типов
Коллекции. Продолжение
Свойства коллекции и возвращаемые значения
ИЗБЕГАЙТЕ предоставления свойств-мутаторов коллекции (т.е. изменяющих коллекцию в целом). Например, пользователи могут заменить содержимое коллекции, сначала очистив её, а затем добавить новое содержимое. Если замена всей коллекции является распространенным сценарием, рассмотрите возможность предоставления метода AddRange.
ИСПОЛЬЗУЙТЕ Collection<T> или подкласс Collection<T> для типов свойств или возвращаемых значений, представляющих изменяемые коллекции. Если Collection<T> не удовлетворяет некоторым требованиям (например, коллекция не должна реализовывать IList), используйте пользовательскую коллекцию, реализуя IEnumerable<T>, ICollection<T> или IList<T>.
ИСПОЛЬЗУЙТЕ ReadOnlyCollection<T>, подкласс ReadOnlyCollection<T> или, в редких случаях, IEnumerable<T> для типов свойств или возвращаемых значений, представляющих коллекции только для чтения. В основном отдавайте предпочтение ReadOnlyCollection<T>, либо пользовательской коллекции (для неё реализуйте ICollection<T>.IsReadOnly, возвращающее true).
В случаях, когда вы уверены, что единственный сценарий, который вы когда-либо захотите поддержать, - это итерация только вперед, вы можете использовать IEnumerable<T>.

⚠️ РАССМОТРИТЕ использование подклассов общих базовых коллекций вместо непосредственного использования коллекций. Это позволяет выбрать осмысленное имя и добавить вспомогательные элементы, которых нет в базовых типах коллекций. Это особенно применимо к высокоуровневым API.
⚠️ РАССМОТРИТЕ возвращение подкласса Collection<T> или ReadOnlyCollection<T> из часто используемых методов и свойств. Это позволит вам добавить вспомогательные методы или изменить реализацию коллекции в будущем.
⚠️ РАССМОТРИТЕ использование коллекции с ключами, если элементы, хранящиеся в коллекции, имеют уникальные ключи (имена, идентификаторы и т. д.). Коллекции с ключами - это коллекции, которые могут индексироваться как целым числом, так и ключом, и обычно реализуются путем наследования от KeyedCollection<TKey, TItem>. Однако они обычно занимают больший объем памяти и не должны использоваться, если требования к памяти перевешивают преимущества наличия ключей.
ИЗБЕГАЙТЕ возвращения null из свойств коллекции или из методов, возвращающих коллекции. Вместо этого верните пустую коллекцию или пустой массив. Общее правило состоит в том, что null и пустые коллекции или массивы должны обрабатываться одинаково.

Снапшоты или Живые Коллекции
Коллекции, представляющие состояние в определенный момент времени, называются снапшотами (моментальными снимками). Например, коллекция, содержащая строки, возвращённые из запроса к базе данных, будет снапшотом. Коллекции, которые всегда представляют текущее состояние, называются живыми коллекциями. Например, коллекция элементов ComboBox является живой коллекцией.
ИЗБЕГАЙТЕ возвращение снапшотов из свойств. Свойства должны возвращать живые коллекции. Акцессоры свойств должны быть очень лёгкими операциями. Для возврата снапшота необходимо создать копию внутренней коллекции в операции порядка O(n).
ИСПОЛЬЗУЙТЕ снапшоты либо живые коллекции IEnumerable<T> для представления коллекций, которые являются нестабильными (то есть могут изменяться без явного изменения коллекции). В общем, все коллекции, представляющие общий ресурс (например, файлы в каталоге), являются нестабильными. Такие коллекции очень трудно или невозможно реализовать как живые коллекции, если только вы не реализуете однонаправленный итератор.

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

Источник:
https://docs.microsoft.com/en-us/dotnet/standard/design-guidelines/
Channel photo updated