Ответ 1
Почему я не могу позвонить
SomeGenericMethod<SomeGenericType<>>
Самый простой ответ: потому что спецификация языка говорит так:
4.4 Построенные типы
Объявление общего типа само по себе обозначает несвязанный общий тип, который используется как "схема" для формирования множества разных типов путем применения аргументов типа. Аргументы типа записываются в угловые скобки (< и > ) сразу же после имени родового типа. Тип, который включает по крайней мере один аргумент типа, называется построенным типом. Построенный тип может использоваться в большинстве мест на языке, на котором может отображаться имя типа. Несвязанный общий тип может использоваться только в типе-выражении (§7.6.11).
4.4.3 Связанные и несвязанные типы
Термин "несвязанный тип" относится к неродному типу или несвязаному родовому типу. Термин связанный тип относится к неродному типу или построенному типу. Несвязанный тип относится к объекту, объявленному объявлением типа. Unbound generic type сам по себе не является типом и не может использоваться как тип переменной, аргумент или возвращаемое значение или как базовый тип. Единственной конструкцией, в которой может ссылаться несвязанный общий тип, является тип выражения (§7.6.11).
Почему спецификация С# так говорит? Потому что спецификация CLI также определяет:
II.9.4 Создание родовых типов
...
CLI не поддерживает частичную реализацию типичных типов. И общие типы не должны казаться неосведомленными где-либо в блоках подписи метаданных.
Хорошо, теперь позвольте остановиться на законных. Фактический ответ на ваш вопрос:
Когда вы вызываете SomeMethod<SomeType>()
в первый раз, CLR вызывает JIT-компилятор, который будет читать SomeMethod<T>
, подставить T
и создать новый исполняемый код, который представляет SomeMethod<SomeType>
.
Когда вы вызываете SomeMethod<SomeOtherType>()
, процесс должен быть повторен, новый код будет создан для SomeMethod<SomeOtherType>
.
Вы не можете просто создать допустимый код для несвязанного общего типа типа Generic<>
. В общем случае JIT не может знать, какое представление кода генерировать, не зная T
.
- Если
T
являетсяstring
, и ваш метод объявляет локальную переменную типаT
, JIT будет выделять пространство стека или использовать регистр с размером собственного указателя. - Если
T
являетсяGuid
, JIT должен будет выделить пространство стека для фиксированного значения 128 бит. Он не может генерировать код, если он не знает, что будетT
.
Следовательно, вы не можете генерировать исполняемый код для несвязанного типа. Почему вы, возможно, могли бы сделать именно это для предоставленного вами фрагмента, для этого потребуется специальная оболочка, и ваш код вызова перестанет работать, если вы добавили локальный T
в метод PrintType
. Поскольку генераторы должны быть повторно использованы для сборок, вызывающий код не может принимать ничего о вызываемом методе.