.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
День двадцать второй. #CodeComplete
4. Утверждения
Утверждение – условие, которое всегда верно; предположение о дизайне, на котором основана процедура.
- документируйте предположения о дизайне через утверждения вместо комментариев;
- документируйте условия, которые всегда верны – в 100% случаев;
- если проверка утверждения терпит неудачу, правильное действие – изменить и перекомпилировать код;
- концептуально утверждение — это помощь при разработке, оно не используется в продакшн-коде (удаляется при компиляции).

Роль утверждений:
- полезны при использовании метода проектирования по контракту (с предусловиями и постусловиями);
- полезны в больших и сложных программах;
- используются как защитная техника, чтобы люди правильно использовали ваш код.

Важно:
Не путайте утверждения с обработкой ошибок!
День двадцать третий. #ЗаметкиНаПолях
Ковариантность и Контравариантность в Обобщениях (начало)

Очень обширная тема, поэтому разобью её на четыре поста:
1. Общие понятия
2. Вариантность в обобщённых интерфейсах
3. Вариантность в обобщённых делегатах
4. Определение вариантных интерфейсов и делегатов

1. Общие понятия
Ковариантность и контравариантность относятся к возможности использования более конкретного (производного) типа или более общего (базового) типа, чем изначально обозначенный. Обобщённые параметры типа поддерживают ковариантность и конртавариантность, чтобы предоставлять большую гибкость в присвоении и использовании обобщённых типов. В следующих примерах допустим, что Animal – базовый класс, а Cat – производный от него.

Инвариантность означает, что вы можете использовать только изначально определённый тип.

Ковариантность позволяет вам использовать производный (более конкретный) тип, чем изначально обозначенный. Вы можете присвоить экземпляр IEnumerable<Cat> переменной типа IEnumerable<Animal>.
Ковариантные параметры типа позволяют вам делать присваивания, похожие на обычный полиморфизм:
IEnumerable<Cat> cats = new List<Cat>();
IEnumerable<Animal> animals = cats;
Класс List<T> реализует интерфейс IEnumerable<T>, поэтому List<Cat> реализует IEnumerable<Cat>. А ковариантный параметр типа делает всё остальное.

Котравариантность позволяет вам использовать более общий (базовый) тип, чем изначально обозначенный. Вы можете присвоить экземпляр делегата Action<Animal> переменной типа Action<Cat>.
Контравариантность выглядит контринтуитивной. В следующем примере создаётся делегат типа Action<Animal>, а затем он присваивается переменной типа Action<Cat>.
Action<Animal> actAnimal = (target)=>{Console.WriteLine(target.GetType().Name);};
Action<Cat> actCat = actAnimal;
actCat(new Cat()); // выводит “Cat”
Лямбда-выражение создаёт метод-делегат, который принимает один параметр типа Animal, и у которого нет возвращаемого значения. Этот делегат может быть присвоен переменной типа Action<Cat>, поскольку параметр T делегата Action<T> контравариантен. Когда делегат типа Action<Animal> вызывается так, как если бы он был типа Action<Cat>, его аргумент должен быть типа Cat.

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

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

Источник: https://docs.microsoft.com/ru-ru/dotnet/standard/generics/covariance-and-contravariance
👍1
День двадцать четвёртый. #ЗаметкиНаПолях
Ковариантность и Контравариантность в Обобщениях (продолжение)

2. Вариантность в обобщённых интерфейсах

Обобщённые интерфейсы с ковариантными параметрами типа
В .NET Framework 4+ несколько обобщённых интерфейсов имеют ковариантные параметры типа, например: IEnumerable<T>, IEnumerator<T>, IQueryable<T>, или IGrouping<TKey,TElement>. Все параметры типа этих интерфейсов ковариантны, поэтому они используются только для типов возвращаемых значений методов. Например, для вышеупомянутых классов Animal и производного от него Cat:
public void Feed(IEnumerable<Animal> animals) {…}

List<Cat> catList = new List<Cat>();
Feed(catList);
IEnumerable<Animal> animalIEnum = catList;
Поскольку параметр типа интерфейса IEnumerable ковариантен, а List<T> реализует IEnumerable<T>, то можно вызывать метод, принимающий IEnumerable<Animal> с параметром List<Cat>, а также присваивать переменной типа IEnumerable<Animal> экземпляр типа List<Cat> без приведения типов.

Обобщённые интерфейсы с контравариантными обобщёнными параметрами типа
В .NET Framework 4+ несколько обобщённых интерфейсов имеют контравариантные параметры типа, например: IComparer<T>, IComparable<T>, или IEqualityComparer<T>. Эти интерфейсы имеют только контравариантные параметры типа, поэтому параметры типа используются только в качестве параметров методов интерфейсов.
Следующий пример иллюстрирует контравариантные параметры типа. В примере объявляется абстрактный класс Shape со свойством Area и класс
ShapeAreaComparer, реализующий IComparer<Shape>. Реализация метода Compare этого интерфейса основана на сравнении площадей фигур по свойству Area, поэтому ShapeAreaComparer может быть использован для сортировки объектов Shape по площади.
Класс Circle наследует от Shape и переопределяет Area. Пример создаёт сортированную коллекцию SortedSet<T> из объектов Circle, используя конструктор, принимающий параметр IComparer<Circle>. Но вместо передачи параметра типа IComparer<Circle>, пример передаёт объект ShapeAreaComparer, реализующий IComparer<Shape>. Пример может передавать параметр базового (более общего) типа, поскольку параметр типа обобщённого интерфейса IComparer<T> контравариантен. Контравариантность позволяет ShapeAreaComparer сортировать коллекцию любого типа или коллекцию объектов разных типов, которые наследуют от Shape.
using System;
using System.Collections.Generic;

abstract class Shape
{
public virtual double Area { get { return 0; }}
}

class Circle : Shape
{
private double r;
public Circle(double radius) { r = radius; }
public double Radius { get { return r; }}
public override double Area
{
get { return Math.PI * r * r; }
}
}

class ShapeAreaComparer : IComparer<Shape>
{
int IComparer<Shape>.Compare(Shape a, Shape b)
{
if (a == null) return b == null ? 0 : -1;
return b == null ? 1 : a.Area.CompareTo(b.Area);
}
}

class Program
{
static void Main()
{
SortedSet<Circle> circlesByArea =
new SortedSet<Circle>(new ShapeAreaComparer())
{
new Circle(7.2),
new Circle(100),
null,
new Circle(.01)
};

foreach (Circle c in circlesByArea)
{
Console.WriteLine(c == null ?
"null" :
"Круг площадью " + c.Area);
}
}
}

Этот код производит следующий вывод:
null
Круг площадью 0.000314159265358979
Круг площадью 162.860163162095
Круг площадью 31415.9265358979
Аналогично можно сделать, например, объект Square, производный от Shape, и создать коллекцию объектов Square, используя тот же класс ShapeAreaComparer:
SortedSet<Square> squaresByArea = new SortedSet<Square>(new ShapeAreaComparer());

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

Источник: https://docs.microsoft.com/ru-ru/dotnet/standard/generics/covariance-and-contravariance
👍2
День двадцать пятый. #ЗаметкиНаПолях
Ковариантность и Контравариантность в Обобщениях (продолжение)

3. Вариантность в обобщённых делегатах
В .NET Framework 4+ обобщённые делегаты Func, такие как Func<T,TResult>, имеют ковариантные возвращаемые типы и контравариантные типы параметров. Обобщённые делегаты Action, такие как Action<T1,T2>, имеют контравариантные типы параметров. Это означает, что делегаты можно присваивать переменным, имеющим производные (более конкретные) типы параметров и (в случае обобщённых делегатов Func) базовые (более общие) возвращаемые типы.

Примечание. Последний параметр типа обобщённых делегатов Func указывает тип возвращаемого значения в сигнатуре делегата. Он является ковариантным, в то время как остальные параметры типа являются контравариантными.

Пусть объявлена следующая переменная:
Func<object, ArgumentException> fn1 = null;
Тогда можно привести её к типу Func с отличающимися параметрами обобщённого типа:
Func<string, Exception> fn2 = fn1; //явного приведения типа не требуется
Ecxeption e = fn2(“ ”);
Такое присвоение можно сделать без приведения типа, поскольку:
- тип параметра ковариантен, а string наследует от object,
- тип возвращаемого значения контравариантен, а Exception – базовый класс для ArgumentException.

Пример показывает, что этот обобщённый делегат может храниться в переменных или параметрах метода, имеющих более конкретные типы параметров и более общие возвращаемые типы, при условии, что все типы делегата сконструированы из обобщённого типа делегата Func<T,TResult>.

Окончание следует…

Источник: https://docs.microsoft.com/ru-ru/dotnet/standard/generics/covariance-and-contravariance
День двадцать шестой. #ЗаметкиНаПолях
Ковариантность и Контравариантность в Обобщениях (окончание)

4. Определение вариантных интерфейсов и делегатов
В .NET Framework 4+ в C# появились ключевые слова, позволяющие помечать параметры обобщённого типа интерфейсов и делегатов как ковариантные или контравариантные.

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

Примечание. Если метод интерфейса имеет параметр типа обобщённого делегата, параметр ковариантного типа этого интерфейса может использоваться для указания параметра контравариантного типа этого типа делегата.

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

Параметры вариантного типа могут иметь только интерфейсы и делегаты. Тип интерфейса или тип делегата может иметь как ковариантные, так и контравариантные параметры типа.

Следующий пример показывает определение ковариантного и контравариантного интерфейсов. Пусть есть абстрактные классы животное (Animal) и млекопитающее (Mammal). И производные от класса млекопитающего классы лошадь (Horse) и кот (Cat). В ковариантном интерфейсе фермы (IFarm), определён метод GetNextAnimal, возвращающий следующее животное. А в контравариантном интерфейсе кормилки для животных (IFeeder), определён обобщённый метод Feed имеющий тип параметра U, ограниченный типом параметра интерфейса T:

abstract class Animal {}
abstract class Mammal : Animal {}
class Horse : Mammal {}
class Cat : Mammal {}

interface IFarm<out T> where T : Animal
{
T GetNextAnimal();
}
interface IFeeder<in T> where T : Animal
{
void Feed<U>(U animal) where U : T;
}

class Program
{
static void Main(string[] args)
{
IFarm<Horse> horsefarm = null;
IFarm<Animal> animalfarm = horsefarm;
Animal animal = horsefarm.GetNextAnimal();

IFeeder<Animal> feeder = null;
feeder.Feed<Mammal>(new Horse());
feeder.Feed<Mammal>(new Cat());
}
}

Источник: https://docs.microsoft.com/ru-ru/dotnet/standard/generics/covariance-and-contravariance
День двадцать седьмой. #CodeComplete
5. Отладка

Иногда бывает полезным проходить новый код по шагам в отладчике, наблюдая за изменением значений переменных. Это может быть формой самостоятельного обзора кода. Кроме того, это помогает уменьшить временной интервал между внесением ошибки и обнаружением её.

Этапы отладки:
1) Стабилизация ошибки. Найдите сценарий, при котором ошибка всегда проявляется.
2) Локализация источника ошибки. Точно найдите место в коде, где возникает ошибка.
3) Исправление ошибки.
4) Проверка и тестирование исправления.
5) Поиск похожих ошибок. Определите тип ошибки и проверьте, нет ли в остальном коде ошибок этого типа.

Советы по обнаружению ошибок
Следующие советы не обязательно применимы ко всем случаям, но могут помочь в некоторых сложных ситуациях:
- используйте все доступные данные для формулирования гипотезы о сути ошибки;
- создайте точные тесты, которые приводят к ошибке;
- воссоздайте ошибку несколькими разными способами;
- по ситуации: сузьте/расширьте сектор кода для поиска ошибки;
- проверьте методы, которые имели ошибки ранее (часто исправление одной ошибки приводит к возникновению другой);
- проверьте недавно изменённый код;
- проверьте код на типичные ошибки (широко распространённые вообще, вроде деления на ноль, нулевых указателей, выхода за границы массива и т.п., либо типичные для вашей области/организации/вас лично);
- поговорите с другим человеком (обзор кода): часто просто объяснение проблемы кому-то другому помогает понять, что не так;
- сделайте паузу: займитесь чем-то отвлечённым на некоторое время;
- прочитайте все предупреждения компилятора: предупреждения компилятора не сообщают об ошибках напрямую, но могут свидетельствовать о недостатках в коде, приводящих к ошибкам в связанных с этим блоком местах;
- проверьте, не является ли это ошибкой проектирования: плохо спроектированный класс или метод иногда проще переписать заново, чем постоянно исправлять;
- проверьте, нет ли ошибки в тестовых данных: написанию тестов обычно уделяют меньше внимания, чем основному коду, поэтому ошибки в тестах встречаются гораздо чаще;
- изначально избегайте ошибок: правильное проектирование и написание кода приводит к значительному сокращению количества ошибок.

Инструкция по исправлению ошибки:
1) убедитесь, что вы понимаете проблему, прежде чем исправлять её;
2) убедитесь, что вы понимаете всю программу, а не только проблемную часть;
3) подтвердите диагноз: убедитесь с помощью различных тестов, что ошибка именно здесь и именно в этом;
4) расслабьтесь :)
5) сохраните исходный вариант кода (системы управления версиями очень помогают);
6) делайте ОДНО исправление за раз: не пытайтесь исправить сразу несколько ошибок одновременно, это приводит к возникновению новых ошибок;
7) проверьте ваше исправление;
8) выполните юнит-тесты (не только относящиеся к исправлению, чтобы проверить, не привело ли исправление к ошибкам в других местах);
9) выполните интеграционные тесты с изменённым кодом;
10) поищите похожие ошибки.

Отладка с применением грубой силы
1) Установите максимальное время на отладку обычным способом, а потом рассмотрите вариант перехода на отладку грубой силой.
2) Составьте список вещей, которые стоит проверить.

Примеры отладки грубой силой:
- переписывание кода с нуля;
- компиляция кода с полной отладочной информацией;
- установка прерывания на каждом исключении;
- создание набора автоматизированных тестов и запуск их на всю ночь;
- выполнение полного обзора проекта/кода;
- точное воссоздание конфигурации оборудования конечного пользователя.
👍1
День двадцать восьмой. #ЗаметкиНаПолях
Явная реализация интерфейсного метода (EIMI)
class SomeType : IDisposable
{
public void Dispose()
{
Console.WriteLine(“public Dispose”);
}
void IDisposable.Dispose()
{
Console.WriteLine(“IDisposable.Dispose”);
}
}
Если в C# перед именем метода вы ставите имя интерфейса, в котором определён этот метод, то вы создаёте явную реализацию интерфейсного метода. Этому методу нельзя указывать область доступа (private или public), однако компилятор, создавая метаданные для метода, делает его private. Поэтому единственный способ вызвать интерфейсный метод – обратиться через переменную этого интерфейсного типа.

Явная реализация методов интерфейса служит двум главным целям:
1) Поскольку она недоступна через экземпляры класса, это позволяет исключить эти методы из общедоступного интерфейса класса. Это, в частности, полезно, когда класс реализует внутренний интерфейс, который не представляет интереса для пользователей класса.
Например, List<T> реализует IList<T> неявно, а IList (необобщённый интерфейс) явно. Это значит, что, когда вы используете класс напрямую, вы увидите только обобщённые методы и не увидите методы, принимающие Object.
Допустим, вы создали экземпляр List<Person>. Если бы IList был реализован неявно, тогда у вас было бы два метода Add, напрямую доступных из класса: Add(Person item) и Add(Object item), что разрушило бы безопасность типов, предлагаемую обобщениями. Вызов list.Add("Foo") успешно бы скомпилировался, поскольку была бы автоматически выбрана перегруженная версия Add(Object).

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

В следующем примере класс ресторана реализует два интерфейса для выдачи вариантов меню: на вынос IWindow и «в ресторане» IRestaurant. Эти интерфейсы имеют одинаковые методы GetMenu, но с разными типами возвращаемого значения. Также ресторан может иметь свою собственную реализацию метода GetMenu, не имеющую отношения к интерфейсным методам. Основная программа запрашивает все три метода, приводя экземпляр класса к интерфейсному типу для вызова явных реализаций интерфейсных методов.
using System;

public abstract class Menu { }
public class WindowMenu : Menu { }
public class RestaurantMenu : Menu { }

public interface IWindow { WindowMenu GetMenu(); }
public interface IRestaurant { RestaurantMenu GetMenu(); }

public sealed class Pizzeria : IWindow, IRestaurant
{
//Явная реализация интерфейсного метода для IWindow
WindowMenu IWindow.GetMenu() => new WindowMenu();

//Явная реализация интерфейсного метода для IRestaurant
RestaurantMenu IRestaurant.GetMenu() => new RestaurantMenu();

//Дополнительный открытый метод, не имеющий отношения к интерфейсам
public string GetMenu() => "Public method";
}

class Program
{
static void Main(string[] args)
{
Pizzeria mp = new Pizzeria();
//открытый метод
Console.WriteLine(mp.GetMenu());

//Метод IWindow.GetMenu
var windowMenu = ((IWindow) mp).GetMenu();
Console.WriteLine(windowMenu.ToString());

//Метод IRestaurant.GetMenu
var restaurantMenu = ((IRestaurant) mp).GetMenu();
Console.WriteLine(restaurantMenu.ToString());

Console.ReadLine();
}
}

Источник: Джеффри Рихтер “CLR via C#”. 3-е изд. – СПб.: Питер, 2012. Глава 13.
👍2
День двадцать девятый. #Оффтоп #МоиИнструменты
В качестве отвлечённой темы коротко опишу вам инструменты, которыми пользуюсь на регулярной основе. Оставим в покое редакторы кода, IDE и прочие программы для администрирования баз данных, поскольку они у каждого свои. А вот остальные помощники могут быть общими у разработчиков в совершенно разных сферах.
SVN и WinMerge
Начнём с контроля версий. У нас в компании используется SVN. Я к нему более-менее привык, поэтому он мне лично нравится больше гораздо более популярного Git. Хотя суть у них одна. Начинали мы больше 10 лет назад без контроля версий, и это был сущий кошмар. Мы реально отправляли файлы с изменениями менеджеру по почте. Теперь даже представить сложно, как люди обходились без контроля версий. В общем виде процесс следующий:
Первая ветка – песочница. Там все разработки и фиксы (исключительно рабочие, никаких недоделок, за редким исключением).
Вторая ветка – staging. Код для тестов на отдельном сервере менеджерами компании перед релизом.
Ну и, собственно, продакшн. Релизятся либо каждый баг/фича по одиночке через SVN Merge, либо собираются сразу несколько в недельный релиз. Тогда обычно мы делаем копию продакшн-ветки, сливаем туда всё, что хотим релизнуть, и тестируем эту ветку на staging-сервере. Потом продакшн-ветка переименовывается в архивную, а эта тестовая копия переименовывается в продакшн. Легко и просто.
WinMerge – полезная утилитка для слияния изменений. С её помощью можно посмотреть отличия между файлами и папками. Программа в большинстве случаев достаточно чётко выделяет строки с различиями, которые можно перенести из одного файла в другой (см. картинку ниже). Она хорошо дополняет SVN, помогая коммитить в SVN только те изменения в файле, которые надо, потому что SVN коммитит либо весь файл, либо ничего. Работает это следующим образом. У меня есть папка с кодом песочницы (в которой выполняются коммиты в SVN) и отдельная рабочая папка, не связанная с SVN. Когда надо что-то залить в SVN я сравниваю эти папки в WinMerge и переношу нужные изменения из рабочей папки в папку песочницы. Таким образом, если в одном файле есть изменения для разных багов, то с помощью WinMerge можно одни изменения перенести, а другие оставить для доработки.
При сравнении папок можно использовать различные фильтры для папок и файлов, например, исключить из сравнения папки .svn или сравнивать только файлы с кодом, пропуская картинки.
День тридцатый.
Сертификат Microsoft. Шаг 1
Итак, спустя месяц после запуска канала, думаю, пришло время хотя бы ознакомиться с тем, что предстоит сделать для получения сертификата по C#. Впоследствии есть планы получить сертификат и по MVC, но начнём с малого :)
Итак, вот собственно страница экзамена https://www.microsoft.com/ru-ru/learning/exam-70-483.aspx Здесь можно посмотреть требования к сдающим, что будет проверяться на тесте, посмотреть варианты для подготовки и даже запланировать сам экзамен. Для России экзамен стоит $80, поэтому к подготовке стоит отнестись серьёзно, просто так попробовать сдать сильно дорого выходит. Кстати, можно попробовать пройти пробный тест вот здесь https://global4.mindhub.com/70-483-programming-in-c-microsoft-official/p/MU-70-483?utm_source=microsoft&utm_medium=certpage&utm_campaign=msofficialpractice, но он тоже платный: доступ $99 на месяц, $109 – на два. Немного странная ценовая политика, как мне кажется. Хотя в штатах сам экзамен стоит $165, в этом случае может и имеет смысл потренироваться)))
Ладно, пока остановимся на требованиях. Кандидаты также должны иметь глубокое понимание следующих тем:
- Управление потоком и событиями программы
- Асинхронное программирование и потоки
- Проверка данных и работа с коллекциями данных, включая LINQ
- Обработка ошибок и исключений
- Работа с массивами и коллекциями
- Работа с переменными, операторами и выражениями
- Работа с классами и методами
- Операторы принятия решения и итеративные операторы
Начнём подготовку…
👍1
День тридцать первый. #CodeComplete
6. Оптимизация кода
Оптимизация кода – это изменение программы на уровне детального кодирования с сохранением семантики кода с целью улучшения производительности.
К оптимизации кода не относятся:
- выбор алгоритма;
- изначальная разработка, нацеленная на высокую производительность.

Правила оптимизации:
1. НЕ ДЕЛАЙТЕ ЭТОГО!
2. (только для экспертов) Не делайте этого… пока. До тех пор, пока у вас нет идеального, но неоптимизированного решения.

Защитный подход к оптимизации кода:
1. Пишите изначальный код, делая акцент на простоту, читаемость, удобство сопровождения, модульность и т.п.
!!! Только если производительность неудовлетворительная !!!
2. Измерьте слабые места производительности.
3. Внедрите изменения с сохранением семантики ПО ОДНОМУ ЗА РАЗ.
4. Измерьте каждое изменение; отмените 2/3 изменений, которые не улучшили производительность.
5. Повторяйте, пока не будет достигнут желаемый результат.

ВАЖНО:
1. Нужно измерить, чтобы быть уверенным.
2. Если вы не измерили, вы НЕ МОЖЕТЕ БЫТЬ УВЕРНЫ!

Общие принципы:
1. Результаты зачастую контринтуитивны. Попытки оптимизировать код, основываясь на теоретических предположениях о скорости работы того или иного кода, часто приводят к замедлению программы.
2. Результаты различаются в зависимости от языков, компиляторов, версий компиляторов.

Не думайте, что вы умнее компилятора и его создателей. В подавляющем большинстве случаев компилятор создаёт более эффективный машинный код из стандартных блоков кода, чем из «хитро оптимизированного» вами кода.
День тридцать второй. #Оффтоп #МоиИнструменты
Продолжу серию об инструментах, которыми я пользуюсь ежедневно. На очереди Jing от TechSmith. Да, штука старая (выпускалась с 2007 по 2012 годы), но тем не менее своё нехитрое дело выполняет.
Очень удобный инструмент для захвата области экрана как в картинку, так и в видео. Пользуемся с коллегами постоянно, когда надо объяснить друг другу, где косяк, что надо исправить или как протестировать исправление (куда нажать и в какой последовательности). Программа позволяет захватить любую область экрана, нажатием сочетания клавиш по вашему выбору. Кстати, прога умеет определять границы окон и даже документов внутри окон, поэтому часто не нужно вымерять размеры до пикселя, она сама предложит заскринить нужную область. Затем в случае с картинкой можно добавить туда выделение, рамки, стрелки и текст (см. картинку ниже). Вполне достаточно, чтобы графически выразить свою мысль, а не писать целый абзац текста вроде «в выпадающем списке в верхнем левом углу выберите пункт XXX, отметьте флажок YYY и слева внизу страницы нажмите кнопку ZZZ».
Потом картинку можно как просто скопировать в буфер, так и сохранить локально или разместить на условно бесплатном хостинге Screencast.com. Условно, потому что в бесплатной версии картинки могут храниться до года, а также ограничен трафик, поэтому, если разместите ссылку на картинку на общедоступном сайте, лимит трафика вы быстро исчерпаете, и картинка отображаться перестанет. Так что это сугубо для личного пользования или внутри небольшой рабочей группы. Размещается картинка/видео в одно нажатие, а после загрузки в ваш буфер обмена скопируется короткая ссылка на неё, которую вы можете использовать.
Видео в бесплатной версии сохраняется только в SWF (да, сейчас браузеры его блокируют, но можно для своих разрешить), но и этого вполне достаточно, если надо показать косяк в анимации или последовательность действий, при которых проявляется ошибка, когда одной картинки недостаточно.
В остальное время утилитка покоится в трее и не раздражает.

UPD: ребята из TechSmith, похоже, выпустили улучшенную версию под названием Snagit. Но она стоит 50 баксов, так что идут они лесом.
👍2
День тридцать третий. #ЗаметкиНаПолях
Использование структур
Думаю, всем знакомо понятие структуры (значимого типа) в C#, его особенности и отличия от класса. Но зачем использовать такой «урезанный» класс?
1) Структуры улучшают производительность при использовании небольших коллекций значимых типов, которые вы хотите сгруппировать вместе. Например, точка с координатами:
struct Point
{
public int x, y;

public Point(int x, int y) {
this.x = x;
this.y = y;
}
}
ВАЖНО! Используйте структуру, если все её поля представляют собой значимые типы. Если хотя бы одно поле ссылочное (например, строковое), используйте класс.

2) В C# использование значимых типов вместо ссылочных приведёт к меньшему количеству обращений к куче, что снизит нагрузку на сборщик мусора, уменьшит количество циклов сборки мусора, и, соответственно, улучшит производительность.

3) При создании структуры переменная, которой она присваивается, содержит реальные данные. В отличие от экземпляра класса, который содержит только ссылку на объект в куче. Когда структура присваивается другой переменной, значения копируются. Таким образом, переменные будут содержать две различные копии одинаковых данных.

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

Кроме того, желательно, чтобы создаваемая вами структура отвечала следующим требованиям:
1. Логически представляла единичное значение
Структуры должны использоваться для представления единичного значения, поскольку они являются значимыми типами, как число. Не имеет смысла хранить экземпляр объекта для числа 5. Это важно, потому что структуры передаются по значению. То есть, когда вы передаёте структуру в параметр метода, всё содержимое структуры копируется, и все изменения структуры внутри метода не влияют на исходную переменную, если только вы не передаёте её явно по ссылке.
2. Имела размер экземпляра не больше 16 байт
Этот совет относится исключительно к производительности. 16 байт считается идеальным размером, поскольку структуры копируются по значению. Считается, что копирование 16 байт в стеке потока занимает примерно то же время, что и обращение в кучу по ссылке.
Этот совет не должен быть определяющим. Вы никогда не должны принимать проектные решения, основываясь на производительности, не проведя измерений. См. пост об оптимизации выше.
3. Была неизменяема
Этот совет основан на том, что структуры копируются по значению. Структуры выглядят, как классы, и вы не получите никакого предупреждения, если будете изменять структуру в методе, передавая её туда по значению. Это ведёт к ошибкам, которые легко сделать и сложно потом отследить, что усложняет сопровождение кода. Если ваши структуры неизменяемые, эта ошибка будет исключена.
4. Не подвержена частой упаковке
Избегайте методов или приведений типов, ведущих к упаковке ваших структур, поскольку это дорогостоящая операция с точки зрения производительности.

Ошибочные предположения о структурах:
1. Структуры всегда размещаются в стеке потока, а не в куче
Структуры на самом деле могут размещаться в куче. Куча и стек потока на самом деле только детали реализации языка C#. В спецификации языка ничто не гарантирует, где объект будет размещён.
2. Использование структур вместо классов исключительно из соображений производительности – это нано-оптимизация
Использование структур вместо классов ради производительности – не очень хорошая идея. Даже размер вашей структуры не имеет значения. Используйте структуры, когда это обосновано семантикой программы.

Источники:
https://samrueby.com/2016/09/05/when-should-you-use-a-struct-instead-of-a-class/
https://www.c-sharpcorner.com/article/what-is-structure-and-when-to-use-in-c-sharp/
👍3
День тридцать четвёртый. #CodeComplete

7. Обработка ошибок
Ситуации, в которых требуется обработка ошибок, возникают, когда значения данных
- выходят за границы,
- необычного характера,
- неверного типа,
- не определены,
- неполные,
- избыточные,
- несоответствующие,
и т.п.

Решения по обработке ошибок:
1. Что программа делает с неверными данными?
2. Нужно ли обрабатывать все возможные ошибки или только самые основные?
3. Какая часть (части) программы ответственны за это?
4. Какие механизмы языка использовать для обработки ошибок?
5. Должны ли все ошибки обрабатываться одинаково?
6. Должны ли все части программы обрабатывать ошибки одинаково?
7. Являются ли эти решения решениями уровня кодирования, проектирования, архитектуры?

Варианты обработки ошибок в списке ниже представлены для использования в системах от наиболее устойчивых до наиболее корректных. В устойчивых системах (пользовательских приложениях) можно выдать хоть какой-нибудь результат, даже неточный. Например, в видеоигре выдать цвет соседнего пикселя или в потоке некритичных данных выдать предыдущее значение. В корректных системах (где безопасность данных критична) важен только точный результат. В противном случае лучше завершить работу приложения, чем выдавать ошибочные данные. Например, машине МРТ лучше выдать ошибку, чем выдать неверный снимок.
1. Использовать нейтральное значение
- null, 0, пустую строку
2. Заменить следующим верным значением
- следующая запись в базе, следующие данные, прочитанные с датчика
3. Выдать тот же ответ, что и в предыдущий раз
- та же температура термометра, ближайшее максимальное/минимальное значение
4. Записать сообщение в лог
- полезно при отладке (стоит обратить внимание на защищённость логов)
5. Вернуть код ошибки
- решение на уровне архитектуры приложения
- некоторые модули могут просто передавать ошибку на верхние уровни и рассчитывать, что она будет обработана там
6. Вызвать объект/процедуру обработки ошибок
- в этом случае обработка ошибок централизована
- вся программа должна знать об этой централизованной возможности и быть связана с ней
7. Вывести сообщение об ошибке
- ошибки обрабатываются локально
- способ обработки отдельных ошибок оставлен на откуп низким уровням (там выбирается наиболее подходящий в конкретном случае)
- имеет потенциальную опасность распространения кода пользовательского интерфейса по всему приложению
8. Завершить работу
- наиболее корректный способ
- используется в приложениях, для которых критична безопасность

Важность обработки ошибок:
- влияет на всю систему приложения (в разных приложениях это 25-75% всего кода);
- влияет на производительность всего приложения;
- это решение на уровне архитектуры приложения. Вы бы не хотели, чтобы обработка ошибок в приложении осуществлялась случайным образом и разным способом в каждом случае.
День тридцать пятый. #Оффтоп #МоиИнструменты
Ещё одна утилитка, которой я, правда, не так часто пользуюсь, как хотелось бы, Action Outline. Программа позволяет делать разнообразные заметки, списки TODO, даже файлы в древовидной структуре. Их можно помечать, перетаскивать, копировать, вставлять, экспортировать в Word и HTML и т.д., и т.п. В общем, очень удобный хранитель мыслей.
День тридцать шестой. #ЗаметкиНаПолях
“Знай свои инструменты”
Чем быстрее мы пишем код, тем лучше. Поэтому собрал тут несколько горячих клавиш и советов по работе в Visual Studio, которые помогут в этом. Не все сочетания работают во всех версиях, тестировал в VS 2017.

Горячие клавиши Панели задач Windows (n – номер по порядку)
1. Запуск программы – Win+Shift+n.
2. Запуск/переключение между программами – Win+n.
3. Отобразить всплывающее меню – Win+Alt+n.
4. Запустить программу от администратора – Win+Shift+Ctrl+n.
5. Размещение окон Windows на экране – Win+<стрелки>.

Visual Studio
1. Глобальный поиск по меню, модулям, документам – Ctrl+Q.
– Для поиска в определённом месте можно использовать фильтр, начинающийся с @
2. Object Browser – Ctrl+Alt+J.
3. Окно Solution Explorer – Ctrl+;.
4. Окно Class View – Ctrl+Alt+C.
5. Окно Toolbox – Ctrl+Alt+X.
6. Окно Перейти к… (Navigate To) – Ctrl+,.
7. Все окна и вкладки можно перетаскивать за заголовок окна и прикреплять к разным границам экрана, а также вынести в отдельное от Visual Studio окно. При этом положение отдельного окна сохраняется после перезапуска.

Solution Explorer
1. Теперь отображает не только проекты и файлы решения, но и классы и члены классов в каждом файле.
2. Отобразить только этот элемент и его дочерние элементы – Правая кнопка мыши на элементе -> Scope to this.
3. Отобразить только этот элемент и его дочерние элементы в новом окне – Правая кнопка мыши на элементе -> New Solution Explorer Window.
4. Фильтры
– Отобразить только открытые файлы – Ctrl+[,O.
– Отобразить только файлы с изменениями (если подключена система контроля версий) – Ctrl+[,P.
5. Отслеживание активного окна редактора в Solution Explorer – Tools -> Options -> Projects and Solutions -> General -> Track Active Item in Solution Explorer.

Навигация
1. Упрощенная навигация (переход к предыдущему/следующему редактированному блоку) – Ctrl+- и Ctrl+Shift+-.
2. Перейти к открывающей/закрывающей скобке – Ctrl+].
3. Перейти к определению или подсмотреть определение – F12 или Alt+F12
4. Найти все ссылки на элемент – Shift+F12.
– Переход по найденным ссылкам – F8.
5. Иерархия вызовов элемента (Call Hierarchy) – Ctrl+K,T.
6. Окно просмотра определения элемента (Code Definition Window) – Ctrl+W,D.
7. Переключение между открытыми файлами – Ctrl+Alt+<стрелка вниз>.

Редактор кода
1. Размер шрифта – Ctrl+Shift+<, Ctrl+Shift+> или Ctrl+<колёсико мыши>.
2. Редактирование по колонкам – Alt+Shift+<стрелки вверх/вниз> или Alt+<выделение мышью>.
3. Найти в документе – Ctrl+F, расширенный поиск – Ctrl+Shift+F.
4. Переход по всем вхождениям элемента – Ctrl+Shift+<стрелки вверх/вниз>.
5. Вставка нескольких последних элементов из буфера обмена – Ctrl+Shift+V.
6. Перемещение строки кода – Alt+<стрелки вверх/вниз>.
7. Дублирование строки кода – Ctrl+D.
8. Сворачивание/разворачивание блоков кода:
- свернуть все методы – Ctrl+M,O
- развернуть все методы – Ctrl+M,P
- свернуть выделенный код в блок […] – Ctrl+M, Ctrl+H
- свернуть/развернуть предыдущий блок или весь код – Ctrl+M,L
- свернуть/развернуть текущий блок – Ctrl+M,M.

Написание кода
1. IntelliSence
- Подсказки: напишите начало элемента или аббревиатуру по заглавным буквам (например, System.AOORE для System.ArgumentOutOfRangeException)
- Переход в режим подсказок (без автозаполнения) – Ctrl+Alt+Space.
2. Сниппеты (стандартные блоки кода: if, for, using, try и т.п.)
- Вставить сниппет - Ctrl+K,X или <ключевое слово>, Tab, Tab.
- Окружить выделенный код сниппетом - Ctrl+K,S.
3. Комментарии
- однострочные комментарии на весь выделенный блок – Ctrl+K,C.
- убрать комментарии с выделенного блока – Ctrl+U,C.
4. Форматирование
- форматировать документ согласно заданным стандартам – Ctrl+K,D.
- форматировать выделенный текст согласно заданным стандартам – Ctrl+K,F.
5. Сортировка/удаление неиспользуемых пространств имен – Ctrl+R, Ctrl+G.
Отладка
1. Выполнить до курсора – Ctrl+F10
- Следующий шаг – F10
- Зайти в… – F11
- Выйти из… – Shift+F11.
2. Точки останова – F9
- В точках останова можно задавать условия останова и действия при достижении точки (например, запись в лог) – Alt+F9, C.

Рефакторинг
1. Советы по рефакторингу – Ctrl+. или Alt+Enter.
2. Инкапсулировать поле – Ctrl+R,E.
3. Извлечь интерфейс из класса – Ctrl+R,I.
4. Извлечь выделенный код в метод – Ctrl+R,M.
5. Удалить/реорганизовать параметры метода – Ctrl+R,V или Ctrl+R,O.
6. Переименовать элемент (во всём проекте) – Ctrl+R,R.

Источник: Курс ITVDN. Visual Studio 2013 Tips & Tricks.
День тридцать седьмой. #CodeComplete
8. Предвидение изменений

Отличительной чертой великих разработчиков является то, что они предвидят больше источников изменений, чем разработчики среднего уровня.

Источники изменений:
- Зависимость от оборудования
- Форматы файлов
- Входящие и выходящие данные
- Нестандартный функционал языка
- Глобальные переменные
- Бизнес-правила
- Последовательность обработки элементов
- Требования, исключённые из текущей версии
- Функционал, запланированный на следующую версию
- И МНОГОЕ ДРУГОЕ…

Основные правила:
1. Используйте абстрактные интерфейсы.
2. Используйте именованные константы вместо жёстко прописанных литералов.
3. Используйте стратегии позднего связывания.
4. Используйте табличные техники (поиск значения в таблице вместо многоуровневых условных операторов).
5. Используйте процедуры вместо копирования кода.
6. Используйте простые процедуры, решающие одну простую задачу.
7. Разделяйте несвязанные между собой операторы.
8. Разделяйте код, реализующий основную функциональность, и специализированный код.
День тридцать восьмой. #МоиИнструменты
Продолжаю описывать мои инструменты. На очереди утилиты, берегущие глаза.
EyeLeo – программа-тренажёр. Напоминает вам периодически отвлекаться от экрана. Каждые 10 минут появляется напоминание сделать какое-нибудь лёгкое упражнение для глаз на 8 секунд: помигать, зажмуриться, повращать глазами или посмотреть в окно. А каждые 50 минут программа напоминает сделать длинный перерыв: отойти от экрана, размяться, дать отдохнуть глазам. Если вам нужно закончить срочное дело, большой перерыв можно отложить на 3 минуты (я иногда откладываю по 5 раз по 3 минуты) или отменить совсем. Все интервалы можно настроить. Например, я сделал большой перерыв не 5, а 10 минут.
А программа f.lux управляет настройками цветов вашего монитора. Вы сообщаете программе ваше местоположение (город), а программа в зависимости от вашего положения и даты подбирает время восхода и захода солнца и соответственно этому изменяет цветовую гамму экрана. Днём она настраивает более холодные (синие) тона, а вечером – более тёплые (красные). Очень помогает тем, что ночью экран не режет глаза. Переход с дневного режима на ночной во время сумерек плавный и длится целый час, поэтому происходит незаметно. В программе куча преднастроенных цветовых схем под разные вкусы, но и их можно изменять.
День тридцать девятый. #TipsAndTricks
Открываю серию постов формата Tips & Tricks (Советы и хитрости) по языку C#. Здесь не будет никакого порядка или логики. Просто всё, что буду находить, и что покажется интересным. Что-то может быть совсем тривиальным, а что-то «очень сильным колдунством».

1. Не забивайте код временными коллекциями, используйте вместо этого Yield
Временный список, содержащий отобранные возвращаемые значения, может создаваться, когда разработчики хотят выбрать значения из коллекции по некоторому условию. Например, рассмотрим следующий код:
public List<int> GetValuesGreaterThan100(List<int> masterCollection)
{
List<int> tempResult = new List<int>();
foreach(var value in masterCollection)
if (value > 100)
tempResult.Add(value);
return tempResult;
}

Чтобы избежать создания временной коллекции, можно использовать ключевое слово yield. Например, код выше можно переписать следующим образом:
public IEnumerable<int> GetValuesGreaterThan100(List<int> masterCollection)
{
foreach (var value in masterCollection)
if (value > 100)
yield return value;
}

Однако для простых случаев, вроде приведённого выше, можно использовать LINQ:
filteredCollection = masterCollection.Where(x => x > 100);