Flexible Coding
149 subscribers
163 photos
2 files
96 links
Download Telegram
Правильный ответ - 1. Почему так? Давайте разбираться

Сравнение делегатов

Сперва нам кажется, что тут всё просто. Func<int, int> function = x => i * x; - это создание новой переменной. На основе документации Microsoft узнаём, что делегат - это reference type, значит они сравниваются по ссылке. А раз мы на каждой итерации цикла создаём новую переменную function - у неё каждый раз будет новый адрес в памяти и Contains всегда вернёт false.

Но делегат - это непростой тип в .NET. Давайте посмотрим, из каких полей он состоит.

Method
Это поле с типом данных MethodInfo, который должен быть вызван при обращении к делегату. В памяти он представлен через внутренние поля _methodPtr и _methodPtrAux, которые являются указателями на конкретную функцию.

Target
Это объект, к которому привязан метод, если он нестатический. То есть если метод — экземплярный, Target указывает на объект, для которого этот метод будет вызван. Если метод статический, Target может быть null.

У делегатов так же переопределён метод Equals. В репозитории reference source можно увидеть полную его реализацию, и нас интересует следующий фрагмент:


// do an optimistic check first. This is hopefully cheap enough to be worth
if (_target == d._target && _methodPtr == d._methodPtr && _methodPtrAux == d._methodPtrAux)
return true;


Приватные поля _target, _methodPtr и _methodPtrAux могут нам указывать на то, что два делегата равны. Они как раз дают нам информацию о методе и привязке метода к объекту. Попробуем вытащить их из делегата (тут нам пригодится немного магии unsafe):


void PrintFunctionInfo(Func<int, int> function)
{
unsafe
{
TypedReference tr = __makeref(function);
IntPtr* ptr = (IntPtr*)*(IntPtr*)&tr;

Console.WriteLine($"Pointer to Delegate: 0x{(ulong)ptr:X}");

IntPtr target = ptr[1];
IntPtr methodPtr = ptr[3];
IntPtr methodPtrAux = ptr[4];

Console.WriteLine($"Target: 0x{(ulong)target:X}");
Console.WriteLine($"MethodPtr: 0x{(ulong)methodPtr:X}");
Console.WriteLine($"MethodPtrAux: 0x{(ulong)methodPtrAux:X}");
}
}

Если мы вызовем этот метод в цикле из примера выше, то обнаружим одинаковые адреса делегата на каждой итерации цикла.

Как вы думаете, почему так?

Flexible Coding
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
Всем привет! Вот и наступило лето, а у нас - итоги прошедшего сезона!

Этой весной мы:

💥 Контролируемо ломали MongoDB - первая и вторая части

😤 Расстраивались из-за Open Source, который становится всё дороже

🧠 Закончили цикл статей про многопоточность (или нет???) - 2, 3 и 4

🔀 Сравнивали делегаты

Пропускали точку с запятой в SQL

🤖 А так же смотрели на ИИ и рисовали схемки в Excalidraw

Flexible Coding