Re: Делегат | |
---|---|
Igor Korolyov Автор Сообщений: 34580 Дата регистрации: 28.05.2002 |
Не только. Автоматически выводятся типы для generic методов и выводятся типы делегатов (конечно если система МОЖЕТ вывести тип - в общем случае это не всегда возможно) для переданных методов (в т.ч. анонимных и лямбд). Анонимный метод или statement-лямбда "сами по себе" не имеют никакого типа. Поэтому их невозможно использовать напрямую - компилятор всегда должен вывести тип - в частности при использовании их как делегатов нужно вывести тип делегата. В строке new Thread(что_то) компилятор видит вызов конструктора класса Thread. Среди всех конструкторов есть лишь 2 принимающих 1 параметр - и это параметр типа ThreadStart либо же ParameterizedThreadStart (оба этих типа - делегаты, но у них разная сигнатура - void () у первого и void (object) у второго). Соответственно компилятор знает, что параметром должен быть либо один либо второй делегат, и ему остаётся только выбрать какой из них подходит в данном конкретном случае. У анонимного метода есть частичная сигнатура - т.е. известно какие параметры он принимает - в твоём примере будет пустой список параметров -> по сигнатуре подходит делегат ThreadStart. Именно его и будет использовать компилятор. Вообще исходный код (синтаксис с лямбдой обрабатывается совершенно аналогично)
В нём же описано поле <>9__0_0 для хранения делегата - оно строго типизировано, и в main прописан код который инициализирует данное поле, если оно пусто (это позволяет не создавать по новой копии делегата при каждом использовании анонимного метода). Переменная arg_20_0 в main на самом деле "виртуальная" - это так декомпилятор восстановил IL код где просто на стек была помещена ссылка на сей делегат. В сами тонкости инициализации, создание экземпляра этого скрытого класса можно не вникать. Главное что мы видим, это совершенно чёткая строгая типизация. Тип делегата однозначно определён, сигнатура анонимного метода - тоже. Ещё раз - типы делегатов не приводятся. В принципе нет для них никаких cast-ов. Компилятор просто сообщает что там где ожидалось ThreadStart он увидел MyDelegate. Да, если бы это были не делегаты, то он бы попытался сделать неявное приведение типов. Возможно даже преуспел бы в этом А так - ну просто не совсем чёткое сообщение. Было бы неразумно расплываться мыслию по древу в сообщении об ошибке, приводя там массу информации о том что где "не совпало" и как с этим быть дальше. Описан лишь самый типичный случай проблемы "несоответствия типов". Похожи сигнатуры "оборачиваемых" методов. Даже более того, они совпадают! Однако сами типы ни разу не идентичны. У них разные имена Или, по твоему,
Потому я и отправил тебя снова читать основы про то что такое тип Нет, потому что компилятор умеет выводить типы делегатов для анонимных методов. Кстати, он умеет и создавать обёртки (делегаты) при указании "просто имени метода" в том месте где должен быть делегат (т.е. выводить тип делегата для НЕ-анонимных методов). Например для событий уже давно необязательно писать "формально полную" конструкцию
На самом деле это по сути одна и та же возможность компилятора... Никак не "преобразовать". Можно лишь создать новый экземпляр [нового] типа делегата и передать ему в конструктор "старый" экземпляр. Тогда в новом экземпляре в списке вызова окажутся те же самые методы, включая анонимные что и в старом (там есть свои небольшие нюансы, но пока что тебе явно не до них будет ). ------------------ WBR, Igor |
Re: Делегат | |
---|---|
S-type Сообщений: 2969 Дата регистрации: 24.04.2004 |
Спасибо за пример, и комментарии к нему. Не с первого раза, но (кажется) смог его "осилить" Вот как создателям C# приходится выкручиваться, что бы "и рыбку съесть, и косточкой не подавиться" - и сильную типизацию соблюсти (скажем так, относительно сильную), и что бы быстро работало. |
Re: Делегат | |
---|---|
Igor Korolyov Автор Сообщений: 34580 Дата регистрации: 28.05.2002 |
Не так, не
, а что бы быстро писалось ленивыми программерами Те же анонимные методы (соответственно и лямбды) субоптимальны. "Вручную" получится меньше инструкций и, тем паче, "сущностей" (автосозданные классы, плюс поля в них для "захвата" переменных, если таковой захват требуется). Про yield return и async/await лучше вообще промолчать - там ГОРЫ кода генерируются Но, с другой стороны, компы нынче быстрые, а время программиста дорогое... Да, конечно, некоторые виды синтаксического сахара практически не влияют на создаваемый код - те же var к примеру. Они лишь замедляют процесс компиляции, что IMHO разумно допустимое зло А некоторые - скажем инициализаторы массивов типа int могут даже и ускорить создаваемый код. ------------------ WBR, Igor |
Re: Делегат | |
---|---|
S-type Сообщений: 2969 Дата регистрации: 24.04.2004 |
Есть код
Можно и так:
Упрощённая конструкция - это ведь синтаксический сахар? Вопрос, почему работает конструкция
Потому, что она преобразуется в
Или, потому что тип MyDelegate унаследован от Delegate? |
Re: Делегат | |
---|---|
Igor Korolyov Автор Сообщений: 34580 Дата регистрации: 28.05.2002 |
Да. Она компилируется в такой же код как и верхняя. Компилятор подставляет вызов конструктора делегата new MyDelegate() передавая ему внутренний рантаймовский указатель на метод (и на сам объект, если метод не статический). Не понимаю смысла вопроса... Работает потому что так работает компилятор в соответствии со спецификацией языка C#... Во что она преобразуется я показал выше - в автогенерируемый класс, метод в этом классе, ряд полей и определённый код инициализации. При чём тут факт того что "тип MyDelegate унаследован от Delegate" я не понимаю. Любой делегат унаследован в частности и от этого типа, равно как и от типа System.MulticastDelegate, только какое это имеет отношение к тому как работает компилятор? Ну это как спросить: "int i = 1; работает потому что int унаследован от типа System.Object?" Да он унаследован, но работает конструкция вовсе не по этой причине. Delegate специальный класс - как и другие классы в Common Type System он не доступен напрямую (нельзя создать свой класс на его основе, хотя он и не "запечатан" aka sealed), и используется только специальными конструкциями CLS языков, в частности синтаксическими конструкциями с ключевыми словами delegate и event в C#. ------------------ WBR, Igor |
Re: Делегат | |
---|---|
S-type Сообщений: 2969 Дата регистрации: 24.04.2004 |
Вот что мне мозг взрывает:
Выведение типов, это когда для выражения определяется тип.
А код
приводит к ошибке компиляции Цитата: Так же, к ошибке приводит
Т.е. для выражения "delegate () { }" то же определяется тип. Но, тип определяется не по тому, что находится в самом выражении! Исправлено 1 раз(а). Последнее : S-type, 06.03.17 13:41 |
Re: Делегат | |
---|---|
S-type Сообщений: 2969 Дата регистрации: 24.04.2004 |
А вот так - работает
|
Re: Делегат | |
---|---|
Igor Korolyov Автор Сообщений: 34580 Дата регистрации: 28.05.2002 |
Ну я уж не знаю как тебе объяснить разницу между КОДОМ и делегатом... У КОДА нет никакого типа - независимо от того обычный это метод или анонимный. У него есть только сигнатура - возвращаемый кодом тип и типы принимаемых им параметров.
У делегата же тип есть. Собственно говоря, делегат это и есть особый вид типов в CTS. Код нельзя никуда передать, получить на него ссылку, получить его "свойства". Делегат - можно. Он для того и создан чтобы можно было передавать "код" как объект. В с++ для этого применялись просто указатели на функции, но они, как и другие указатели нетипобезопасны. Делегат - это типобезопасный указатель на некоторый код. В переменную типа делегат вполне можно присвоить значение другой переменной - лишь бы она была совместимого типа (про Variance лучше пока даже не вспоминать...) Что при этом может "взрывать мозг" я не понимаю... И да, система автоматически создаёт делегаты в НЕКОТОРЫХ случаях. msdn.microsoft.com ------------------ WBR, Igor |
Re: Делегат | |
---|---|
Igor Korolyov Автор Сообщений: 34580 Дата регистрации: 28.05.2002 |
Не определяется тип для анонимного метода (так же как и для лямбды) - нет у него никакого типа! Вот если этот метод будет обёрнут в делегат, то у него тип будет. ------------------ WBR, Igor |
Re: Делегат | |
---|---|
S-type Сообщений: 2969 Дата регистрации: 24.04.2004 |
Вот именно - "совместимого типа"! В выражении:
в переменную типа MyDelegate присваивается переменная какого типа? Само по себе выражение "delegate () { }" ведь типа не имеет? Вот в чём когнитивный диссонанс |
Re: Делегат | |
---|---|
Igor Korolyov Автор Сообщений: 34580 Дата регистрации: 28.05.2002 |
Естественно типа MyDelegate. Выше цитата из MSDN, я даже специально выделил ключевое...
В данном случае переменной присваивается НЕ другая переменная, а именно свежесоздаваемый делегат, оборачивающий этот самый анонимный метод. ------------------ WBR, Igor |
Re: Делегат | |
---|---|
S-type Сообщений: 2969 Дата регистрации: 24.04.2004 |
Можно написать так:
На msdn.microsoft.com указаны 4-ре конструктора. Я правильно понимаю, что в случае
происходит неявное преобразование:
Надо ли явно указывать:
Исправлено 1 раз(а). Последнее : S-type, 10.04.17 12:25 |
Re: Делегат | |
---|---|
S-type Сообщений: 2969 Дата регистрации: 24.04.2004 |
Оборачивающий, это
Может всё таки, неявно преобразующий (на подобии)
Исправлено 1 раз(а). Последнее : S-type, 10.04.17 12:24 |
Re: Делегат | |
---|---|
Igor Korolyov Автор Сообщений: 34580 Дата регистрации: 28.05.2002 |
Ещё раз - делегаты НЕ "преобразуются". Ни явно, ни неявно. Можно лишь создать новый делегат взяв "указатель на метод" у старого/существующего делегата. Либо же просто взяв "указатель на метод" который (метод) получается из анонимного блока кода.
Иногда делегаты создаются неявно - вот как раз new Thread(RunMe); Это и есть неявное создание делегата, этот код полностью аналогичен new Thread(new ThreadStart(RunMe)); ------------------ WBR, Igor |
Re: Делегат | |
---|---|
S-type Сообщений: 2969 Дата регистрации: 24.04.2004 |
Почитал - да, ты прав. написано, что экземпляры делегатов всегда неизменяемы (immutable). Почему же тогда компилятор не ругается на
? Приведение типа, это ведь преобразование? Исправлено 1 раз(а). Последнее : S-type, 10.04.17 15:17 |
Re: Делегат | |
---|---|
Igor Korolyov Автор Сообщений: 34580 Дата регистрации: 28.05.2002 |
Видимо потому что там ничего никуда не "приводится".
Компилятор вообще-то явно говорит что в данном месте конструкция (MyDelegate) лишняя. Равно как, к примеру
Разница лишь в том, что в случае делегата компилятор в любом случае будет вызывать конструктор этого самого делегата, передавая туда "указатель" на анонимный метод (внутри скомпилированной сборки метод будет уже совсем не "анонимным"). Указатель на метод внутри IL имеет тип "native int" - и конструктор делегата принимает именно этот тип (в частности поэтому нельзя его напрямую вызывать/создавать). Твои попытки "подсказать" компилятору что за этим самым "указателем типа native int" (анонимный метод) будет метод который требуется обернуть в делегат типа MyDelegate излишни - компилятор и сам это знает исходя из левой части выражения. Есть ситуации когда компилятор этого не знает (не может вывести тип делегата для оборачивания какого-либо метода - анонимного или нет, без разницы). Например вызов Control.BeginInvoke() или твой старый пример с вызовом obj.GetType(). Когда компилятор видит что ожидается тип Delegate (НЕ "конкретный тип-наследник" скажем MyDelegate а именно "базовый" - т.е. можно передать туда абсолютно любой делегат) или тип Object (т.е. подходит вообще любой тип), он попадает в тупик. Он не может создать делегат типа Delegate или MulticastDelegate, т.к. это абстрактные типы, разрешены лишь их "наследники". А какой именно тип-наследник выбрать? Вот тут-то подсказка в виде (MyDelegate) и спасает - хотя никаких "приведений типа" она не делает, но компилятору уже становится ясно что нужен именно тип MyDelegate и он совершенно спокойно генерирует код new MyDelegate(оборачиваемый_метод). ------------------ WBR, Igor |
© 2000-2024 Fox Club  |