Все, что было сказано до сих пор, верно. Вот как это работает под прикрытием: делегат имеет два поля: указатель на исполняемый код и поле типа объекта, представляющего этот параметр (вы можете передать делегат методу экземпляра).
Когда вы вызываете этот делегат, центральный процессор извлекает указатель на код в регистр и затем «вызывает» этот указатель. Инструкции вызова не должны использовать постоянные значения. Процессор может перейти к переменной области в памяти.
События - это просто делегаты плюс два метода-обертки для присоединения нового делегата или удаления существующего. Смущает то, что у делегатов есть третье поле, которое я пропустил: делегат! делегаты формируют связанный список. Это называется MulticastDelegate, и это мерзость. Когда вы вызываете делегата, это может привести к вызову нескольких методов с одинаковой подписью. Вот как работают события. Событие - это отдельное поле типа делегата.
Теперь забудьте о многоадресных делегатах, потому что они не имеют отношения на практике.