Skip to content

Instantly share code, notes, and snippets.

Если в коде поставить точку останова, то debugger дойдет до нее и остановится, как это происходит?

Я буду описывать отладку в windows. Когда мы ставим точку останова, debugger, еще до начала процесса знает на какой инструкции будет стоять эта точка останова(Это реализуется с помощью pdb файлов, в которых хранится маппинг имен переменных и IL инструкций методов, которые однозначно преобразуются байт код. Этот маппинг корректно работает при отключенных jit оптимизациях) И заменяет ее инструкцией int 3(interrupt 3).

Встречая эту инструкцию процессор генерирует прерывание, а windows exception с кодом 0X8000003. Информация об этом исключение передается процессу, совершающему отладку и тот должен ее как-то обработать.

Windows предоставляет такое API для отладки :

Как загружается сборка

Когда JIT компилирует метод, ему необходимо получить информацию об используемых в методе типах, например:

Тогда он идет в TypeRef сборки(служебная таблица в метаданных), и находит там такую информацию:

Это определение типа, которое добавляется в метаданные только если в коде идет работа с типом, а потом в AssemblyRef, где уже лежит вся информация о типе. Таким образом он собирает всю информацию вместе: полное имя типа и полное имя сборки, далее версия, культура и публичный токен. Потом пробует загрузить сборку в AppDomain, если она еще не была загружена, если сборка не имеет strong name, то сборка будет идентифицироваться только по имени(без версии, culture и publictoken'a).

AppDomain это набор сборок. Изначально при запуске приложения создан один дефолтный домен приложения. Вообще изначально домены создавались для разделения выполняемого в одном windows процессе кода. Можно было бы выполнять код в разных процессах, но само создание нового процесса - дорого по времени. Поэтому CLR позволяет создавать домены приложений в одном процессе. При этом домену не выделяются какие-то дополнительные потоки(насколько я понимаю).

Из кода можно создать домен так:

var ad = AppDomain.CreateDomain("new created domain", null, null); // name , securityInfo, appDomainInfo

Далее в этом домене можно создавать типы, существующие в текущем домене вызывом метода:

var createdInAnotherDomainType = ad.CreateInstanceAndUnwrap("assemblyWhichContainsSomeTypeName","someTypeName")
    public interface IValidator
    {
        ValidatorType Type { get; }
    }

    public struct EqualValidator : IValidator
    {
        public string Left;
        public string Right;

Внедрение зависимостей в .net core сделано очень просто, встроенный контейнер поддерживает не так много кейсов, поэтому достаточно легкий, сама структура же и все интерфейсы хорошо продуманы, так что это позволяет подключать множество сторонних контейнеров, которые для поддержки системы DI в .net core, должны реализовать интерфейс IServiceProvider и IServiceProviderFactory

Устройство контейнера

Как я уже писал, он очень прост, так что в нем можно разобраться не тратя большое количество времени. Сначала рассмотрим IServiceCollection, это очень простая последовательность инкапсулирующая ServiceDescriptor'ы. Service Descriptor это также один из контрактов которые сторонние контейнеры должны принять, а именно - всю информацию о зависимостях они получат используя IServiceCollection. Теперь посмотрим, какие возможности предлагает IServiceCollection:

Содержимое ServiceDescriptor:

Как CLR ищет сборки

При попытке создать динамическую сборку я столкнулся с проблемой. У меня был проект, который генерировался утилитой, я программно компилировал его используя Roslyn api.
Это апи требует передачи Metadata References, ссылок на сборки, которые нужны компилируемому проекту, я добавил всё из папки ProgramFiles/dotnet/sdk/3.2.1/ и все из папки с опубликованным вручную проектом. Таким образом я разрешил все статические привязки и "добавил" в манифест сборки всю необходимую информацию о зависимых сборках(имя, token, culture и т.д)

Все успешно скомпилировалось, roslyn не вернул никаких сообщений об ошибках компиляции, и я загрузил сборку используя csharp Assembly.LoadFrom(pathToAssembly). Далее, когда попытался рефлексивно обратиться к ее типам(csharp assembly.GetTypes().ToArray()) получил сообщение об ошибке :

В итоге при попытке обратиться к информации о типах, CLR нужно было подгрузить сборку в кото

При проекции в DTO, если проекция происходит в IEnumerable, то entity framework не будет вытягивать эти данные при запросе, а вытянет после при обращении к этой коллекции, поэтому и возникает n+1 запрос. Чтобы избежать этого, можно поменять тип свойства на массив, тогда при проекции automapper сам сделате ToArray:

public class Dto{
  public SomeCollection[] arr;
}
public class Entity{
  SomeEntity[] SomeCollection{get;set;}
}

Как реализовано получение Comparer'a в методе List.Distinct? В чем преимущества такого получения?

В List не ограничения на T, такого как where T:IEqualityComparer<T> поскольку не для всех кейсов нужно реализовывать IEqualityComparer, в большинстве случаев лист используется просто как хранилище элементов, да и реализация типом IEqualityComparer может противоречить Single Responsibility. Поэтому в листе Comparer явно не задан. Поэтому, если не передать компарер в метод явно будет использоваться свойство EqualityComparer.Default, инициализация которого происходит только один раз. (Инициализация статического свойства происходит при первом обращении).

В случае generic статического свойства оно будет инициализироваться по одному разу для каждого типа T. Таким образом проседание производительности будет только один раз, в далее все будет отрабатывать быстро. Сам компарер просматривает тип T, реализует ли он ```IEqua

Почему происходит упаковка структуры при обращении к методу базового класса?

Базовый класс в данном случае это ValueType(отличие классов от структур - структуры наследуются от ValueType, классы от Object), и методы этого класса принимают первым параметром ValueType или Object(оба ссылочные. Поэтому для вызова метода базового типа структура упаковывается и передается неявным this. Но вообще наследование структур запрещено, так что наследование возможно только от ValueType'a.

Почему для простых типов не происходит упаковки при вызове их базовых методов

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

Чем плох enum в данном контексте и как обойти проблему излишних аллокаций при использовании enum'ов (https://habr.com/ru/post/343430/)

Проблема в том, что Enum не может переопределить методы базового класса (equals, gethashcode), насколько я понимаю, сложность в том, что En

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

Для генерации у ссылочных типов учитывается поток на котором запрашивается вызов GetHashCode, у каждого потока есть свой генератор hash кодов. Для структур дописать

Как генерация GetHashCode связана с SyncBlockIndex'ом

При первом вызове функции GetHashCode результат запоминается в SyncBlockIndex'e, если на этот объект не была наложена блокировка. Если была, то он запоминается в таблице объектов синхронизации по индексу равному числу записанному в SyncBlockIndex'e. Если сначала был вызван метод GetHashCode, а потом объект был взят в блокировку, тогда Hash Code записанный в SyncBlockIndex'e копируется в выделенный объект синхронизации, а в SyncBlockIndex записывается индекс этого блока синхронизации в таблице

Если блок синхронизации долго не применяется, то он будет собран сборщиком мусора и hashcode снова запишется в заголовок объекта, что на данный момент зап