Делегаты в .NET: как они построены? - PullRequest
14 голосов
/ 14 марта 2010

Осматривая делегатов в C # и .NET в целом, я заметил некоторые интересные факты:

При создании делегата в C # создается класс, производный от MulticastDelegate, с конструктором:

.method public hidebysig specialname rtspecialname instance 
   void .ctor(object 'object', native int 'method') runtime managed { }

Это означает, что он ожидает экземпляр и указатель на метод. И все же синтаксис построения делегата в C # предполагает, что у него есть конструктор

new MyDelegate(int () target)

, где я могу распознать int () как экземпляр функции (int *target() будет указателем на функцию в C ++). Очевидно, что компилятор C # выбирает правильный метод из группы методов, определенной именем функции, и создает делегат. Итак, первый вопрос: откуда компилятор C # (точнее Visual Studio) выбирает эту сигнатуру конструктора? Я не заметил никаких особых атрибутов или чего-то, что могло бы сделать различие. Это какая-то магия компилятора / visualstudio? Если нет, допустима ли конструкция T (args) target в C #? Мне не удалось получить что-либо с ним, чтобы скомпилировать, например .:

int () target = MyMethod;

недопустимо, поэтому делает что-либо с MyMetod, например, вызов .ToString() для него (ну, в этом есть некоторый смысл, поскольку технически это метод group , но я думаю, что должна быть возможность явно выбрать метод путем приведения, например, (int())MyFunction. все это чисто волшебство компилятора? Глядя на конструкцию через рефлектор, выявляется еще один синтаксис:

Func CS $ 1 $ 0000 = новый Func (null, (IntPtr) Foo);

Это соответствует дизассемблированной подписи конструктора, но не компилируется!

Последнее интересное замечание: классы Delegate и MulticastDelegate имеют еще один набор конструкторов:

.метод семейства hidebysig specialname rtspecialname экземпляр void .ctor (класс System.Type target, строка 'method') cil managed

Где происходит переход от указателя экземпляра и метода к типу и имени метода строки? Можно ли это объяснить ключевыми словами runtime managed в сигнатуре пользовательского конструктора делегата, т. Е. Среда выполнения выполняет свою работу здесь?

РЕДАКТИРОВАТЬ : хорошо, так что я должен переформулировать то, что я хотел сказать этим вопросом. По сути, я предполагаю, что в построении делегата задействована не только магия C # компилятора / CLR, но и некоторая магия Visual Studio , поскольку Intellisense отключает новый синтаксис при предложении аргументов конструктора и даже скрывает один из них. (например, Reflector не использует этот синтаксис и конструктор, в этом отношении).

Мне было интересно, верно ли это утверждение, и имеет ли синтаксис экземпляра функции более глубокий смысл в C # или это просто какой-то постоянный формат, реализованный магической частью Visual Studio для ясности (что имеет смысл, так как выглядит неверным) C #)? Короче говоря, если я внедрял Intellisense, я должен сделать что-то магическое для делегатов или я мог бы создать предложение с помощью какого-нибудь умного механизма?

ЗАКЛЮЧИТЕЛЬНОЕ РЕДАКТИРОВАНИЕ : так что, по общему мнению, это действительно против магии. Видя другие примеры (см. Комментарий Марка Гравелла) такого поведения VS, я убедился, что это так.

Ответы [ 3 ]

12 голосов
/ 15 марта 2010

Первый аргумент разрешается из ссылки на объект (или null для статических методов); там нет магии.

Re аргумент second , однако - это неуправляемый указатель (native int); короче говоря, нет альтернативного прямого C # синтаксиса, который может использовать этот конструктор - он использует специальную инструкцию IL (ldftn) для разрешения функции из метаданных. Однако вы можете использовать Delegate.CreateDelegate для создания делегатов с помощью отражения. Вы также можете использовать IL Emit (DynamicMethod и т. Д.), Но это не весело.

0 голосов
/ 15 марта 2010

Это всего лишь предположение, поэтому не стреляйте в меня, если я ошибаюсь, но я думаю, что Intellisense получает сигнатуру для целевого метода из метода Invoke, определенного для делегата. Отражатель ясно показывает, что метод Invoke на System.Action<T> имеет вид:

[MethodImpl(0, MethodCodeType=MethodCodeType.Runtime)]
public virtual void Invoke(T obj);

Что совпадает с предложением о подписи, предложенным Intellisense. Магия - Intellisense, при определении типа делегата смотрит на метод Invoke и предлагает конструктор, который принимает цель, которая соответствует ему.

0 голосов
/ 15 марта 2010

Сначала вы определяете делегата (вот как Visual Studio знает сигнатуру целевого метода):

delegate void MyDelegate();

Затем вы создаете экземпляры делегатов следующим образом:

MyDelegate method = new MyDelegate({method name});

// If this was the method you wanted to invoke:
void MethodToInvoke()
{
    // do something
}

MyDelegate method = new MyDelegate(MethodToInvoke);

C # автоматически выбирает метод, соответствующий подписи делегата.

Редактировать: Когда Intellisense Visual Studio показывает вам предложение int () target, он показывает подпись методов C #, которые вы можете использовать. Компилятор C # переводит представление C # в IL. Реализация IL будет выглядеть иначе, поскольку IL не является языком стиля C, а компилятор C # предоставляет синтаксический сахар для абстрагирования от деталей реализации.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...