.NET Разработчик
6.53K subscribers
442 photos
3 videos
14 files
2.12K links
Дневник сертифицированного .NET разработчика. Заметки, советы, новости из мира .NET и C#.

Для связи: @SBenzenko

Поддержать канал:
- https://boosty.to/netdeveloperdiary
- https://patreon.com/user?u=52551826
- https://pay.cloudtips.ru/p/70df3b3b
Download Telegram
День сто пятьдесят шестой. #ЗаметкиНаПолях
Сериализация. Окончание
Пример использования для глубокого копирования
При создании копии объекта возникает проблема с тем, что создаётся поверхностная копия. Даже метод MemberwiseClone в System.Object создает поверхностную копию нового объекта. Копирование объекта выполняется свойство за свойством, если свойство является значимым типом, то копируются данные по битам, а если свойство является ссылочным типом, то копируется ссылка на исходный объект. Другими словами, поле или свойство объекта-клона будет ссылаться на тот же объект.
Одним из способов решить эту проблему, то есть создать глубокую копию объекта, будет использование сериализации. Следующий класс создаёт метод-расширение для глубокого копирования объектов через сериализацию. Замечания:
- объекты копирования должны быть помечены атрибутом Serializable;
- мы используем упомянутый выше класс StreamingContext, задавая его свойству State значение StreamingContextStates.Clone (то есть мы сериализуем объект с целью его копирования).
public static class ObjectExtension
{
public static T DeepCopy<T>(this T objSource)
{
using (MemoryStream stream = new MemoryStream())
{
BinaryFormatter formatter = new BinaryFormatter();
formatter.Context = new StreamingContext(StreamingContextStates.Clone);
formatter.Serialize(stream, objSource);
stream.Position = 0;
return (T)formatter.Deserialize(stream);
}
}
}

Рассмотрим пример использования этого метода расширения. Допустим, у нас есть простой класс:
[Serializable]
private class Person
{
public string Name { get; set; }
}
В примере ниже мы составляем список объектов этого класса. Затем делаем поверхностную копию этого списка, а затем глубокую копию. Отличие в том, что, если изменить значение свойства одного из объектов, то изменение затронет как изначальный список, так и его поверхностную копию. А вот глубокая копия останется неизменной:
var jeff = new Person { Name = "Jeff" };
var kris = new Person { Name = "Kristin" };

// изначальный список
var persons = new List<Person> { jeff, kris };
// поверхностная копия
var shallowCopy = new List<Person>(persons);
// глубокая копия
var deepCopy = objects.DeepCopy();
// изменяем свойство объекта
jeff.Name = "Bob";

Если вывести в консоль значения свойств Name объектов из каждого списка, мы получим:
persons: Bob, Kristin
shallowCopy: Bob, Kristin
deepCopy: Jeff, Kristin

Источник: Джеффри Рихтер “CLR via C#”. 3-е изд. – СПб.: Питер, 2012. Глава 24.
День сто пятьдесят седьмой. #Оффтоп
Рефакторинг унаследованного (легаси) кода.
Как обычно, гуляя по ютубу, наткнулся на вот такое интересное видео https://www.youtube.com/watch?v=XejfFBvODxc
Лекция разработчика из Додо Пиццы (ВНЕЗАПНО!) по рефакторингу. Как исправлять, поддерживать и покрывать тестами код, при этом сохраняя старый код и не рискуя ничего поломать. Далеко не со всеми методами лично я согласен, однако, есть и некоторые интересные приёмчики.

Коротко об основных приёмах:
1. Сохраняем сигнатуры
Даже самые ужасные на вид сигнатуры из легаси методов безопаснее просто скопировать для использования в новых методах, чем производить какие-либо манипуляции с аргументами.
2. Метод/класс росток
Если нужно внести дополнение в старый метод, вставляем туда новый метод, который покрываем тестами. Хотя бы новый код будет протестирован.
3. Метод/класс обёртка
Для добавления функциональности к старому методу создаём новый метод, в котором вызываем старый и добавляем новый код. В отношении классов используем шаблон Декоратор: наследуем от старого класса, добавляя функциональность.
4. Извлечение и переопределение
Если метод использует функциональность, которую нельзя или трудно протестировать явно (запись в консоль/файл/БД, отправка писем клиентам, отправка платежей и т.п.), извлекаем вызов каждого такого метода в новый метод, который делаем protected virtual. Далее создаём тестовый класс, унаследованный от старого класса, в котором переопределяем нужные методы, чтобы их можно было протестировать (например, вместо записи в файл сохраняем в память). Таким образом можно протестировать логику старого класса через тестовый, не вызывая ненужных нам методов.
День сто пятьдесят восьмой. #ВопросыНаСобеседовании
Самые часто задаваемые вопросы на собеседовании по .NET
3. Что такое Общая Система Типов (CTS)?
Общая система типов определяет, как типы объявляются, используются и управляются в общеязыковой среде выполнения (CLR), а также является важной частью поддержки межъязыковой интеграции во время выполнения. CTS выполняет следующие функции:
1. Создает структуру, которая помогает обеспечить межъязыковую интеграцию, безопасность типов и высокопроизводительное выполнение кода.
2. Предоставляет объектно-ориентированную модель, которая поддерживает полную реализацию многих языков программирования.
3. Определяет правила, которым должны следовать языки, что помогает обеспечить взаимодействие объектов, написанных на разных языках.
4. Предоставляет библиотеку, которая содержит примитивные типы данных (такие как Boolean, Byte, Char, Int32 и UInt64), используемые при разработке приложений.

Типы в .NET
Все типы в .NET делятся на значимые и ссылочные.
Значимые типы - это типы данных, объекты которых представлены фактическим значением объекта. Если экземпляр значимого типа присвоен переменной, этой переменной даётся свежая копия значения.
Ссылочные типы - это типы данных, объекты которых представлены ссылкой (аналогично указателю) на фактическое значение объекта. Если ссылочный тип присвоен переменной, эта переменная ссылается (указывает) на исходное значение. Копия значения не делается.
Категории типов:
- Классы
- Структуры
- Перечисления
- Интерфейсы
- Делегаты

Определения типов
Определение типа включает в себя:
- Любые атрибуты, определенные для типа
- Доступность (видимость) типа
- Имя типа
- Базовый тип типа
- Любые интерфейсы, реализуемые типом
- Определения для каждого из членов типа

Члены Типа
Среда выполнения позволяет вам определять элементы вашего типа, которые определяют поведение и состояние типа:
- Поля
- Свойства
- Методы
- Конструкторы
- События
- Вложенные типы

Источник: https://docs.microsoft.com/en-us/dotnet/standard/base-types/common-type-system
День сто пятьдесят девятый. #ВопросыНаСобеседовании
Самые часто задаваемые вопросы на собеседовании по .NET
4. Что такое Общеязыковая Спецификация (CLS)?
.NET Framework не зависит от языка. Это означает, что как разработчик вы можете писать на одном из множества языков, поддерживаемых .NET Framework, таких как C#, C++/CLI, Eiffel, F#, IronPython, IronRuby, PowerBuilder, Visual Basic и т.п. Вы можете получить доступ к типам и членам библиотек классов, разработанных для .NET Framework, не зная языка, на котором они были изначально написаны, и не следуя никаким соглашениям исходного языка. Если вы разработчик компонента, доступ к вашему компоненту может получить любое приложение .NET Framework независимо от его языка.
Чтобы полностью взаимодействовать с другими объектами, написанными на любом языке, объекты должны предоставлять вызывающим их объектам только те функции, которые являются общими для всех языков. Этот общий набор функций определяется Общеязыковой Спецификацией (CLS), которая представляет собой набор правил, применимых к сгенерированным сборкам.
Если ваш компонент соответствует CLS, он гарантированно совместим с ней и доступен из кода в сборках, написанных на любом языке программирования, который поддерживает CLS. Чтобы создать компонент, который не зависит от языка, вам нужно применить правила соответствия CLS только к общедоступному интерфейсу вашего компонента. Ваша приватная реализация не обязана соответствовать спецификации.
Чтобы сообщить компилятору, что ваша сборка должна соответствовать CLS при компиляции, нужно применить атрибут CLSCompliant. Например:
using System;
[assembly: CLSCompliant(true)]
public class Person
{
private UInt16 _age = 0;
public UInt16 Age
{ get { return _age; } }
}

Попытка компиляции этого примера отобразит предупреждение компилятора: warning CS3003: Type of 'Person.Age' is not CLS-compliant (Тип 'Person.Age' не совместим с CLS). Вы можете сделать класс Person совместимым с CLS, изменив тип свойства Age с UInt16 на Int16, которое является CLS-совместимым 16-разрядным целым числом со знаком. Вам не нужно менять тип приватного поля _age:
using System;
[assembly: CLSCompliant(true)]
public class Person
{
private Int16 _age = 0;
public Int16 Age { get { return _age; } }
}

Источник: https://docs.microsoft.com/en-us/dotnet/standard/language-independence-and-language-independent-components
День сто шестидесятый. #ВопросыНаСобеседовании
Самые часто задаваемые вопросы на собеседовании по .NET
5. Что такое управляемый код?
Управляемый код - это код, выполнение которого управляется средой исполнения. В этом случае рассматриваемая среда исполнения называется Common Language Runtime или CLR, независимо от реализации (Mono или .NET Framework или .NET Core). CLR отвечает за распознавание управляемого кода, компиляцию его в машинный код и последующее выполнение. Кроме того, среда выполнения предоставляет несколько важных сервисов, таких как автоматическое управление памятью, границы безопасности, безопасность типов и т.д.
В этом смысле программы на C/C++ называются «неуправляемым кодом». Здесь программист отвечает практически за всё. Такая программа - это, по сути, двоичный файл, который операционная система загружает в память и исполняет. Всё остальное, от управления памятью до соображений безопасности, является ответственностью программиста.
Управляемый код написан на одном из языков высокого уровня, который можно запускать поверх .NET, таких как C#, Visual Basic, F# и другие. Когда вы компилируете код, написанный на этих языках, с помощью соответствующего компилятора, вы не получаете машинный код. Вы получаете код на промежуточном языке (Intermediate Language), который среда исполнения затем компилирует и выполняет. C++ является единственным исключением из этого правила, поскольку он также может создавать собственные неуправляемые двоичные файлы, которые работают в Windows.
Конечно, CLR позволяет переходить через границы между управляемым и неуправляемым миром, и есть много примеров кода, который делает это, даже в библиотеках базовых классов. Это называется совместимостью (interoperability или interop). Это позволяет вам, например, обернуть неуправляемую библиотеку и обратиться к ней. Однако важно отметить, что при этом, когда код пересекает границы среды выполнения, фактическое управление выполнением снова оказывается в руках неуправляемого кода и, таким образом, попадает под те же ограничения.
Подобно этому, C# позволяет вам использовать неуправляемые конструкции, такие как указатели, непосредственно в коде, используя так называемый небезопасный контекст, который обозначает фрагмент кода, выполнение которого не управляется CLR.

Источник: https://docs.microsoft.com/en-us/dotnet/standard/language-independence-and-language-independent-components
День сто шестьдесят первый. #TipsAndTricks
28. Советы по отладке в Visual Studio
1. Нажатие F10 для сборки и запуска проекта для отладки (вместо F5) автоматически остановит выполнение на первой строке вашего кода. Не нужны никакие точки останова. (C VS2005)
2. Reattach to process (Shift+Alt+P) очень полезно, когда вам нужно подключаться к одному и тому же процессу снова и снова. (C VS2017 v15.8)
3. Синий кружок с восклицательным знаком на значке точки останова обозначает переключение потоков при проходе по шагам в отладчике (см. картинку). (C VS2013)

Источник: https://devblogs.microsoft.com/visualstudio/visual-studio-tips-and-tricks/
День сто шестьдесят второй. #TipsAndTricks
29. Редактор Visual Studio
1. Легко оборачивайте элементы HTML в <div>, нажав Shift+Alt+W. Вставляемый <div> выбирается текущим элементом, так что вы можете легко отредактировать его, заменив на любой желаемый элемент, и закрывающий тег изменится автоматически. (С VS2017)
2. Скопируйте любой фрагмент кода JSON и вставьте его как строго типизированный класс .NET в любой файл кода C# или VB (см. картинку). (С VS2013)
3. Вам не нужно писать кавычки вокруг имён свойств JSON. При добавлении двоеточия после имени свойства Visual Studio вставит кавычки автоматически. (С VS2015)
4. Подсказки IntelliSense можно сделать полупрозрачными, просто удерживая клавишу Ctrl после того, как они появляются. (С VS2010)
5. Вместо перепечатывания скобок для отображения подсказки по параметрам метода, нажмите Ctrl+Shift+Пробел, чтобы отобразить подсказку по текущей перегруженной версии метода. (С 2010)

Источник: https://devblogs.microsoft.com/visualstudio/visual-studio-tips-and-tricks/
День сто шестьдесят третий. #юмор
День сто шестьдесят четвёртый. #ВопросыНаСобеседовании
Самые часто задаваемые вопросы на собеседовании по .NET
6. Что такое IL?
Common Intermediate Language (сокращённо CIL или IL) — промежуточный язык, разработанный Microsoft для платформы .NET Framework. Ранее язык назывался Microsoft Intermediate Language (MSIL), однако был переименован для создания стандарта ECMA-335.
IL - это набор независимых от процессора инструкций, которые генерируются компилятором языка при компиляции проекта. В частности, код IL генерируют все компиляторы .NET, входящие в Visual Studio (C#, Managed C++, Visual Basic .NET, Visual J# .NET). Код IL не является исполняемым, но далее обрабатывается средой CLR или другими средами выполнения в исполняемый код. Сборки приложения .NET написаны на IL.

Особенности:
Инструкции IL соответствуют коду, написанному на языке .NET и используемому для загрузки, хранения, инициализации и вызова методов на объектах, а также для арифметических и логических операций, управления потоком исполнения, прямого доступа к памяти, обработки исключений и других операции. JIT-компилятор в CLR преобразует код IL в машинный код, который затем исполняется операционной системой.
По синтаксису и мнемонике IL напоминает язык ассемблера. Его можно рассматривать как ассемблер виртуальной машины .NET. В то же время IL содержит некоторые достаточно высокоуровневые конструкции, повышающие его уровень по сравнению с ассемблером для любой реально существующей машины, и писать код непосредственно на IL легче, чем на ассемблере для реальных машин. Поэтому IL можно рассматривать как своеобразный «высокоуровневый ассемблер».

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

Источники:
-
https://ru.wikipedia.org/wiki/Common_Intermediate_Language
-
http://www.dotnetfunda.com/interviews/show/3948/what-is-msil
День сто шестьдесят пятый. #юмор
День сто шестьдесят шестой. #ВопросыНаСобеседовании
Самые часто задаваемые вопросы на собеседовании по .NET
7. Что такое JIT-компилятор?
В .NET Framework все языки .NET используют CLR, что решает проблему установки отдельных сред выполнения для каждого из языков программирования. Если на компьютере установлена общеязыковая среда исполнения Microsoft .NET, она может работать на любом языке, совместимом с Microsoft .NET. Языки .NET компилируются в промежуточный язык IL. Перед выполнением IL должен быть преобразован компилятором Just-In-Time (JIT) в машинный код, который представляет собой специфические для процессора команды на той же компьютерной архитектуре, что и JIT компилятор.
Процесс компиляции промежуточного кода в машинный называется JIT-компиляцией Just-In-Time, потому что он не происходит до тех пор, пока сборка не будет размещена на целевой машине. Причина, по которой сборка не компилируется заранее, заключается в том, что можно использовать конкретный JIT-компилятор для вашей ОС и типа процессора. В результате сборка компилируется в максимально быстрый машинный код, оптимизируется и улучшается для вашей конкретной конфигурации.
Вместо того чтобы использовать время и память для преобразования всего IL кода в машинный код, он преобразуется по мере необходимости во время исполнения и сохраняется в результирующем машинном коде, чтобы он был доступен для последующих вызовов.
Среда исполнения предоставляет и другой режим компиляции, называемый генерацией кода во время установки. Режим генерации кода во время установки преобразует IL в машинный код, как это делает обычный JIT-компилятор, но одновременно конвертирует большие блоки кода, сохраняя результирующий машинный код для использования при последующей загрузке и исполнени сборки. Как часть компиляции IL в машинный код, код должен пройти процесс проверки, если администратор не установил политику безопасности, которая позволяет коду обходить проверку. Верификация проверяет IL и метаданные, чтобы выяснить, можно ли определить, что код безопасен по типу, то есть известно, что он имеет доступ только к тем ячейкам памяти, к которым у него есть права доступа.

Источник: https://www.c-sharpcorner.com
День сто шестьдесят седьмой. #МоиИнструменты
Пишите Код Лучше и Быстрее с Помощью Анализаторов Кода Roslyn
Roslyn, платформа компилятора .NET, помогает вам обнаруживать ошибки еще до того, как вы запустите свой код. Одним из примеров является анализатор проверки орфографии, встроенный в Visual Studio. Допустим, вы создаете статический метод и неправильно написали слово static как statc. Вы сможете увидеть эту орфографическую ошибку до того, как запустите свой код.
Анализаторы Roslyn также могут отображать советы по автоматическому исправлению кода с помощью значка лампочки Visual Studio, который позволяет немедленно исправить код.
Но что, если бы вы могли обнаруживать еще больше ошибок?
Коллекции анализаторов Roslyn обеспечивают более подробный анализ кода, но не поставляются со стандартными инструментами Visual Studio. Начиная с Visual Studio 2017 версии 15.8, их можно установить в проект как пакет NuGet. Просто поищите в онлайн пакетах Microsoft.CodeAnalysis.FxCopAnalyzers.
После установки пакета вы можете настроить правила анализатора в окне Solution Explorer. Он появится в блоке References или Dependencies. Если развернуть блок анализаторов, а затем развернуть одну из сборок, вы сможете увидеть все наборы правил. Вы можете просмотреть свойства набора правил, включая его описание, уровень критичности по умолчанию и ссылку на статью с подробным описанием правил, в окне Properties (по правому щелчку мыши или нажав Alt+Enter).
Правила имеют несколько уровней критичности:
- “i” в кружке – Информационное (Info)
- “!” в треугольнике – Предупреждение (Warning)
- “x” в кружке – Ошибка (Error)
- “i” в кружке на светлом фоне – Скрытое (Hidden)
- “↓” в кружке – Отсутствует (None)

Вы можете установить критичность наборов правил в обозревателе решения. Щёлкните правой кнопкой на наборе правил и выберите Set Rule Set Severity. Например, если вы установите серьезность набора правил на Warning, вы получите предупреждение в своем коде там, где эти правила нарушаются. Можно добавлять собственные анализаторы кода, инструкция здесь: https://docs.microsoft.com/ru-ru/dotnet/csharp/roslyn-sdk/tutorials/how-to-write-csharp-analyzer-code-fix

Источник: https://devblogs.microsoft.com/dotnet/write-better-code-faster-with-roslyn-analyzers/
День сто шестьдесят восьмой. #ВопросыНаСобеседовании
Самые часто задаваемые вопросы на собеседовании по .NET
8. Что такое переносимый исполняемый файл (Portable Executable)?
Переносимый исполняемый (PE) файл – это стандартный файл, который операционная система может загрузить и исполнить. В системе Windows два типа приложений: консольные (Console User Interface, CUI) и графические (Graphical User Interface, GUI). В .NET Framework каждая программа выполняется в операционной системе с помощью CLR.

Управляемый PE-файл состоит из следующих частей:
1. Заголовок PE32(+)
Хранит стандартную информацию, ожидаемую Windows.

2. Заголовок CLR
Cодержит старшую и младшую версии CLR, для которых скомпонован модуль, ряд флагов и маркер MethodDef, указывающий метод точки входа в модуль. Кроме того, заголовок содержит размер и смещение некоторых таблиц метаданных, расположенных в модуле, а также может содержать сигнатуру строгого имени сборки, если используется строгое имя.

3. Метаданные
Блок двоичных данных, состоящий из нескольких таблиц: определений, ссылок и манифеста.
Таблица определений:
- ModuleDef – имя модуля с расширением, идентификатор версии модуля.
- TypeDef – записи для каждого типа, определённого в модуле: имя, базовый тип, флаги доступа и указатели на записи MethodDef, PropertyDef и EventDef этого типа.
- MethodDef – список методов, определённых в модуле: флаги (private, static, virtual и т.п.), сигнатура и смещение в модуле, по которому находится IL-код модуля. А также ссылки на записи в ParamDef для параметров модуля.
- FieldDef – список полей: флаги и тип поля.
- ParamDef – список параметров: флаги (in, out, ref и т.п.), тип и имя.
- PropertyDef – список свойств: имя, флаги, тип и вспомогательное поле.
- EventDef – список событий: имя и флаги.
Таблица ссылок:
- AssemblyRef – записи для каждой сборки, на которую ссылается модуль: имя, версия, региональные стандарты, маркер открытого ключа, флаги и хэш.
- ModuleRef – список PE-модулей, реализующих типы, на которые ссылается текущий модуль: имя файла сборки, расширение.
- TypeRef – список типов, на который ссылается модуль: имя типа и ссылка на его местоположение.
- MemberRef - список членов (поля, метода, свойства или события), на которые ссылается модуль: имя члена, сигнатура и ссылка на TypeRef.
Таблица манифеста:
- AssemblyDef – содержит запись, если модуль идентифицирует сборку: имя сборки, версия, региональные стандарты, флаги, хэш и открытый ключ издателя (может быть null).
- FileDef – список PE-файлов или файлов ресурсов, входящих в состав сборки: имя, расширение, хэш, флаги.
- ManifestResourceDef – список ресурсов, включённых в сборку: имя, флаги, индекс таблицы FileDef, указывающий на файл или поток с ресурсом (ресурс может быть как отдельным файлом, например, изображением, так и храниться в виде потока в PE-файле).
- ExportedTypesDef – список открытых типов, экспортируемых всеми PE-модулями сборки: имя типа, индекс таблицы FileDef и индекс таблицы TypeDef.

4. IL-код
Скомпилированный IL-код модуля.

Источник: Джеффри Рихтер “CLR via C#”. 3-е изд. – СПб.: Питер, 2012. Глава 2.
День сто шестьдесят девятый. #ВопросыНаСобеседовании
Самые часто задаваемые вопросы на собеседовании по .NET
9. Что такое сборка?
Сборка - это логическая группировка одного или нескольких управляемых модулей или файлов ресурсов. Это самая маленькая единица с точки зрения многократного использования, безопасности и управления версиями. Управляемые модули сборки могут быть файлами dll или exe в зависимости от типа проекта.
1. Закрытые (private) сборки
Это сборка, которая используется только одним приложением. Предположим, у нас есть проект, в котором мы ссылаемся на DLL, поэтому при создании этого проекта эта DLL будет скопирована в папку bin нашего проекта. Эта DLL становится закрытой сборкой в нашем проекте. Обычно библиотеки DLL, предназначенные для конкретного проекта, являются закрытыми сборками.
2. Совместно используемые (shared) сборки
Сборки, которые можно использовать более чем в одном проекте, известны как совместно используемые. Такие сборки обычно устанавливаются в GAC. Сборки, установленные в GAC, становятся доступными для всех приложений .NET на этом компьютере.

GAC
GAC (Global Assembly Cache, Глобальный Кэш Сборок) - это место на диске, где располагаются совместно используемые сборки. До .NET 3.5 GAC располагался в C:\Windows\assembly, в более поздних версиях - в C:\Windows\Microsoft.NET\assembly\GAC_MSIL.

Установка сборки в GAC
GAC обладает особой структурой и содержит множество вложенных каталогов, имена которых генерируются по определённому алгоритму. Ни в коем случае нельзя копировать файлы сборок в GAC вручную. Для установки сборки в GAC сначала необходимо создать строгое имя для сборки, поскольку в GAC устанавливаются только сборки со строгим именем. Все остальные сборки называются сборками со слабым (нестрогим) именем, и их нельзя сохранить в GAC.
Для создания строгого имени нужно получить пару ключей с помощью утилиты Strong Name SN.exe, поставляемой в составе .NET Framework SDK и Visual Studio. После этого сборка компилируется вместе с файлом ключей. При указании такого файла при компиляции компилятор подписывает сборку закрытым ключом, а открытый ключ встраивает в манифест сборки.
После этого подписанную сборку со строгим именем можно установить в GAC с помощью утилиты GACUtil.exe. Подписание файла закрытым ключом гарантирует, что именно держатель соответствующего открытого ключа является производителем сборки. Кроме того, рассчитывается хэш содержимого файлов и сохраняется в таблице FileDef манифеста сборки. Это предохраняет сборку от изменения. При установке сборки в GAC хэши файлов рассчитываются заново и сравниваются со значениями из таблицы FileDef. Если хотя бы одно не совпадает, значит файлы были изменены, и установка сборки в GAC закончится неудачей.

Источники:
-
https://www.c-sharpcorner.com
- Джеффри Рихтер “CLR via C#”. 3-е изд. – СПб.: Питер, 2012. Глава 3.
День сто семидесятый. #BestPractices
Рекомендации по использованию методов расширения. Начало
1.
У вас должна быть веская причина использовать метод расширения вместо экземплярного метода. Одна из таких причин - когда вы имеете дело с не принадлежащими вам типами, например, из сторонней библиотеки.
2. Используйте метод расширения, когда функциональность наиболее соответствует расширяемому типу. Например, разумно иметь метод расширения date.AddDays(numDays), который расширяет тип DateTime. Но не имеет смысла делать наоборот: days.AddToDate(date), который расширяет int.
3. Добавляйте методы расширения к интерфейсам, чтобы добавить общие функциональные возможности к классам, не имеющим общего базового класса. Яркий пример – методы LINQ, расширяющие интерфейс IEnumerable.
4. Используя серию методов расширения с тем же типом возврата, вы можете добиться синтаксиса функционального программирования. Например:
public static IEnumerable<Customer> AboveAge(this IEnumerable<Customer> customers, int age)
{
return customers.Where(c => c.Age > age);
}
public static IEnumerable<Customer> OnlyMale(this IEnumerable<Customer> customers)
{
return customers.Where(c => c.Gender == "Male");
}
public static IEnumerable<Customer> OnlyFemale(this IEnumerable<Customer> customers)
{
return customers.Where(c => c.Gender == "Female");
}

// использование:
var potentialBuyers = customers.AboveAge(42).OnlyFemale();

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

Замечание: как и в большинстве руководств по стилю кодирования, вышеизложенное является сугубо личным мнением.

Источник:
https://michaelscodingspot.com/
День сто семьдесят первый. #BestPractices
Рекомендации по использованию методов расширения. Окончание

5.
Вы можете использовать методы расширения для разделения ответственности, когда вы не хотите внедрять некоторую зависимость в расширяемый тип. Например, вы можете расширить Customer с помощью такого метода:
public static AdjustLastSeen(this Customer customer, TimeZoneManager timeZoneManager) { … }
В примере выше, если вы не хотите внедрять зависимость Customer от TimeZoneManager, вы можете добиться этого с помощью метода расширения. Обратите внимание, что в подобных случаях методы расширения могут быть не лучшим вариантом.
6. Если вы не уверены, какой тип расширять, не используйте методы расширения. Например, чтобы построить дом из кирпича (brick) и раствора (mortar), я могу расширить кирпич с помощью brick.BuildHouse(mortar). Или можно расширить раствор с помощью mortar.BuildHouse(brick). Поскольку ни один из методов не является более подходящим, чем другой, вероятно, методом расширения использовать не стоит.
7. Избегайте наличия состояния в ваших методах расширения. Лучше всего вообще избегать наличия состояния в статических классах, потому что это значительно затрудняет тестирование.
Одно из возможных исключений - если вы хотите использовать мемоизацию (кеширование входных и выходных данных для повторного использования результатов). Но это совсем другая история.
8. Избегайте расширения примитивных типов. Есть несколько причин для этого. С одной стороны, будет очень трудно найти метод, который наиболее соответствует примитивному типа. Кроме того, Visual Studio будет всегда показывать этот метод расширения в IntelliSense при каждом использовании этого примитивного типа.
9. Группируйте методы расширения для одного типа в одном классе. Так будет легче найти эти методы в коде. Кроме того, у всех них должно быть много общего, поскольку они относятся к одному и тому же типу.

Замечание: как и в большинстве руководств по стилю кодирования, вышеизложенное является сугубо личным мнением.

Источник:
https://michaelscodingspot.com/
172. applied-tdd.gif
140.4 KB
День сто семьдесят второй. #юмор
День сто семьдесят третий. #ВопросыНаСобеседовании
Самые часто задаваемые вопросы на собеседовании по .NET
10. Что такое сборщик мусора?
Мусор состоит из объектов, созданных во время выполнения программы в управляемой куче, которые больше не доступны программе.
Сборщик мусора (Garbage Collector - GC) является частью .NET Framework, которая выделяет и освобождает память для ваших приложений .NET. CLR управляет выделением и освобождением управляемого объекта в памяти. Программисты C# никогда не делают этого напрямую, этим управляет сборщик мусора.
Объекты .NET размещаются в области памяти, называемой управляемой кучей. Они будут автоматически уничтожены сборщиком мусора. Распределение кучи происходит только при создании экземпляров классов. Это избавляет программиста от необходимости вручную удалять объекты, которые больше не требуются для выполнения программы. Такое повторное использование памяти помогает уменьшить объем памяти, необходимый программе для работы. Объекты размещаются в куче непрерывно, один за другим. Это очень быстрый процесс, так как это просто добавление значения к указателю.
Процесс освобождения памяти называется сборкой мусора. Он освобождает только те объекты, которые больше не используются в приложении. Корень - это место в хранилище, содержащее ссылку на объект в управляемой куче. CLR проверяет объекты в управляемой куче, чтобы определить, доступны ли они из приложения (имеют ли корень), и создает граф доступных объектов в куче.
Предположим, что управляемая куча содержит набор объектов с именами A, B, C, D, E, F и G (см. рисунок ниже). Во время сборки мусора эти объекты проверяются на наличие активных корней. После построения графа недоступные объекты (в данном случае C и F) помечаются как мусор.
После этого объекты помеченные, как мусор удаляются из памяти. В этот момент оставшееся пространство в куче сжимается, что, в свою очередь, заставляет CLR изменить набор активных корней приложения, чтобы обращение происходило к правильному месту в памяти. Наконец, указатель следующего объекта изменяется, чтобы указывать на следующее свободное место.

Поколения объектов
Для повышения производительности освобождения памяти управляемая куча разделена на сегменты, называемые «поколениями». Существует 3 поколения: 0, 1 и 2. Идея поколений проста: чем дольше объект существует, тем дольше он доступен и тем реже сборщик мусора будет проверять его корень.
Когда объекты только что созданы, они помещаются в Поколение 0 (Gen 0).
Когда Gen 0 заполняется, GC выполняет сборку мусора. При этом удаляются все недоступные объекты из кучи. Все доступные объекты переходят в Поколение 1 (Gen 1). Сбор мусора в Gen 0 - довольно быстрая операция.
Когда Gen 1 заполняется, выполняется сборка мусора Gen 1. Все объекты, которые сохранились после сборки мусора, повышаются до Gen 2. При этом также выполняется сборка в Gen 0.
Когда заполнено Gen 2, GC выполняет полную сборку мусора. Сначала выполняется сборка в Gen 2, затем в Gen 1 и Gen 0. Если памяти для новых объектов всё ещё недостаточно, GC выбрасывает исключение OutOfMemory.
Во время полной сборки мусора GC должен пройти через все объекты в куче, поэтому этот процесс может оказать большое влияние на системные ресурсы.

Источник: https://www.c-sharpcorner.com
День сто семьдесят четвёртый. #BestPractices
Когда использовать LINQ с синтаксисом запроса вместо методов. Начало
Выражения LINQ можно написать в двух вариантах - синтаксис запроса и синтаксис методов. Например, следующие запросы идентичны:
var numbers = Enumerable.Range(1, 100); //1,2,...,100
//Запрос:
var query = from n in numbers
where n % 3 == 0
select n * 2;
//Методы:
var method = numbers
.Where(n => n % 3 == 0)
.Select(n => n * 2);

По некоторым причинам большинство разработчиков (включая меня) чувствуют себя более комфортно с синтаксисом методов. Возможно, причина в том, что программисты привыкли к регулярным вызовам методов. В отличие от синтаксиса запроса, который является своего рода новым языком (ну, вроде как SQL, но он все же менее привычен, чем C#).
Нет никакого явного преимущества одного синтаксиса над другим. Фактически любой синтаксис запроса может быть преобразован в синтаксис методов.
Однако есть несколько случаев, когда синтаксис запроса лучше, то есть он делает код более читабельным:

1. Ключевое слово "let"
Ключевое слово let позволяет сохранить результат для последующего использования в запросе. Вот пример:
var querySyntax =
from person in people
let yearsWorking = GetYearsWorking(person)
where yearsWorking > 4
orderby yearsWorking
select person.Name;

var methodSyntax = people
.Select(person => new {
YearsWorking = GetYearsWorking(person), Name = person.Name })
.Where(x => x.YearsWorking > 4)
.OrderBy(x => x.YearsWorking)
.Select(x => x.Name);
Как видите, с синтаксисом запросов все красиво и чисто. Синтаксис метода не является ужасным, но он требует создания анонимные классов и использования их в остальной части запроса. Поэтому, когда вы хотите «сохранить» значение в дополнение к запрашиваемой коллекции, рассмотрите возможность использования предложения let.

2. OrderBy по нескольким полям
Как в синтаксисе запроса, так и в синтаксисе методов вы можете легко выполнить упорядочивание по нескольким полям. Например, мы можем упорядочить людей по возрасту, а затем по доходу, где возраст - первый, а доход - второй. Это означает, что люди с одинаковым возрастом будут упорядочены по доходам:
var people = new Person[]
{
new Person() {Age = 20, Income = 5000, Name = "Peter"},
new Person() {Age = 30, Income = 8000, Name = "Alfredo"},
new Person() {Age = 30, Income = 7000, Name = "Bo"},
new Person() {Age = 20, Income = 4000, Name = "Jo"},
new Person() {Age = 20, Income = 6000, Name = "Amanda"},
new Person() {Age = 30, Income = 5500, Name = "Maxim"},
};

var querySyntax = from person in people
orderby person.Age, person.Income
select $"{person.Age} {person.Income} {person.Name}";
var methodSyntax = people
.OrderBy(person => person.Age)
.ThenBy(person => person.Income)
.Select(person => $"{person.Age} {person.Income} {person.Name}");

//result
//20 4000 Jo
//20 5000 Peter
//20 6000 Amanda
//30 5500 Maxim
//30 7000 Bo
//30 8000 Alfredo
Я признаю, что оба варианта хороши, и разница не так велика, как в других случаях. Но синтаксис запроса все же выглядит лучше.

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

Замечание: как и в большинстве руководств по стилю кодирования, вышеизложенное является сугубо личным мнением.

Источник:
https://michaelscodingspot.com/
👍2
День сто семьдесят пятый. #BestPractices
Когда использовать LINQ с синтаксисом запроса вместо методов. Продолжение

3. Несколько источников данных
Если у вас есть несколько источников данных для запроса, синтаксис запроса будет предпочтительнее. Причина в том, что вы можете использовать ключевое слово from несколько раз. Например:
var rows = Enumerable.Range(1, 3); //1,2,3
var columns = new string[] { "A", "B"};
var querySyntax = from row in rows
from col in columns
select $"cell [{row}, {col}]";
var methodSyntax =
rows.SelectMany(row => columns, (r, c) => $"cell [{r}, {c}]");
foreach (var cell in methodSyntax)
{
Console.WriteLine(cell);
}
//Вывод:
//cell[1, A]
//cell[1, B]
//cell[2, A]
//cell[2, B]
//cell[3, A]
//cell[3, B]
Цель здесь - получить коллекцию со всеми возможными комбинациями из 2 источников. С синтаксисом запроса код прост и не требует пояснений. А синтаксис методов вовсе не так очевиден для понимания.

4. GroupBy или group
Метод расширения GroupBy очень похож на group в запросе. Например:
var names = new string[] { "Alex", "George", "Alfredo", "Bo", "Greg", "Maxim" };
var querySyntax = from name in names
group name by name[0];
var methodSyntax = names
.GroupBy(name => name[0], name => name);
foreach (var pair in querySyntax)
{
var names1 = string.Join(", ", pair.ToList());
Console.WriteLine($"Key = {pair.Key} Names = {names1}");
}
//Вывод:
//Key = A Names = Alex, Alfredo
//Key = G Names = George, Greg
//Key = B Names = Bo
//Key = M Names = Maxim
Опять же, синтаксис методов не совсем ясен. Что означает второй параметр? Конечно, если немного подумать, смысл становится понятен. Но я не хочу думать, когда я смотрю на код, я хочу читать его как книгу. Детскую книгу, если это возможно.

5. Объединения
Как правило, всякий раз, когда вам нужно объединить коллекции, синтаксис запроса будет более читаемым. Вот внутреннее объединение, например:
var categories = new Category[]
{
new Category() {Name="Игрушки", ID=1},
new Category() {Name="Приборы", ID=2}
};
var products = new Product[]
{
new Product(){Name="Кукла", CategoryID=1},
new Product(){Name="Блендер", CategoryID=2},
new Product(){Name="Утюг", CategoryID=2},
new Product(){Name="Медведь", CategoryID=1}
};
var querySyntax =
from category in categories
join prod in products on category.ID equals prod.CategoryID
select new { ProductName = prod.Name, Category = category.Name };
var methodSyntax = categories.Join(products,
category => category.ID,
prod => prod.CategoryID,
(category, prod) => new {ProductName = prod.Name, Category = category.Name});
В синтаксисе методов сравниваются 2-й и 3-й параметры объединения. Но в отличие от синтаксиса запроса, это не совсем понятно из кода. В синтаксисе запросов это намного понятнее.

Замечание: как и в большинстве руководств по стилю кодирования, вышеизложенное является сугубо личным мнением.

Источник:
https://michaelscodingspot.com/
👍1