Generic или не Generic

362
02 января 2017, 21:08

В обсуждении моего ответа на вопрос возник один спорный момент.

Допустим имеем обобщенный класс и три метода в нем:

class SomeClass<T>
{
    //не обобщенный метод
    public void DoSomething(int x) 
    { 
        Console.WriteLine("True non generic method"); 
    }
    //не обобщенный метод с параметром обобщенного типа класса
    public void DoSomething(T x)
    { 
        Console.WriteLine("Indirectly generic method"); 
    }
    //явно  обобщенный метод
    public void DoSomething<U>(U x)
    { 
        Console.WriteLine("True generic method"); 
    }
}

Собственно вопрос - является ли не обобщенный метод, с параметром обобщенного типа класса, обобщенным методом?

Answer 1

Всё же, как вы догадались, ответ необобщённый (non-generic). Потому что есть определение, что такое обобщённый метод и всё.

Метод считается обобщённым, когда у него есть свой параметр типа. Если у него его нет, то это необобщённый метод.

У обобщённого класса метод может заимствовать параметр тип для возвращаемого значения или для типа параметра (не путайте тип параметра с параметром типа, хотя этот каламбур, наверняка, наводит путаницу), но это не сделает его обобщённым, если у него нету своего параметра типа.

И ваши исследования генерируемого IL совершенно не противоречат мои доводам. Но я бы всё равно не смешивал терминологию языка программирования и детали его реализации. Я не считаю это корректным.

Подкину тему для размышлений: иногда невиртуальные методы вызываются как виртуальные. Просто генерируется инструкция callvirt в некоторых случаях для невиртуальных методов и всё. Можно было бы также спросить "Может их следует называть неявно виртуальные?" (Но тут скорей очевидно, что это просто делать реализации).

Смущение для разработчиков

Но для начинающих (и не только) разработчиков — это, по крайней мере поначалу, странно. К примеру, все привычные члены Dictionary необобщённые: Add(TKey, TValue), ContainsKey(TKey), TryGetValue(TKey, TValue), Remove(TKey) и т.д.. И это многих удивляет, даже иногда тех, кто пользовался годами этим словарём. Тип обобщённый, но методы — нет. У List дела обстоят интересно: у него только один обобщённый метод ConvertAll<TOutput>(Converter<T, TOutput>), остальные — нет.

Answer 2

В MSDN такие методы называются не обобщенными методами обобщенного класса, что ни чем не лучше моей формулировки в вопросе. В спецификации такой вариант тоже не рассматривается отдельно. Ок, раз ни где ни чего конкретно не написано спросим у самой CLR, в конце концов именно ей исполнять все что мы понаписали.

Итак, начнем с объявления класса

.class public auto ansi beforefieldinit ConsoleApplication.SomeClass`1<T>

Интересный момент: `1 - цифры после апострофа означают число параметров типа, но главная задача этой прибавки - расширение имени класса. Таким образом у нас может быть два класса с одинаковым именем и разным количеством обобщающих параметров включая не обобщенный класс без параметров.

Объявления методов (тело опускаю, т.к. не важно в данном случае):

Не обобщенный метод

.method public hidebysig 
    instance void DoSomething (
        int32 x //тип указан явно
    ) cil managed 

Явно обобщенный метод

.method public hidebysig 
    instance void DoSomething<U> (
        !!U x //обратите внимание на два восклицательных знака
    ) cil managed 

Два восклицательных знака сообщают JITу что конкретный тип нужно искать в обобщающих параметрах метода

Не обобщенный метод с параметром обобщенного типа класса

.method public hidebysig 
    instance void DoSomething (
        !T x //тут только один восклицательный знак
    ) cil managed 

Один восклицательных знака сообщают JITу что конкретный тип нужно искать в обобщающих параметрах класса.

Ок, уже что-то, теперь взглянем на то, как эти методы вызываются:

Не обобщенный метод

call instance void class ConsoleApplication.SomeClass`1<int32>::DoSomething(int32)

тип параметра указывается явно

Явно обобщенный метод

call instance void class ConsoleApplication.SomeClass`1<int32>::DoSomething<int32>(!!0)

тип параметра указывается ссылкой на параметр с индексом 0 в списке обобщающих параметров метода

Не обобщенный метод с параметром обобщенного типа класса

call instance void class ConsoleApplication.SomeClass`1<int32>::DoSomething(!0)

тип параметра указывается ссылкой на параметр с индексом 0 в списке обобщающих параметров класса

Получается, что в текущей реализации мы имеем два вида обобщенных методов - явные и неявные. В обоих случаях JITу потребуются дополнительные действия при компиляции данных методов для разрешения типов, т.к. в обоих случаях при вызове указана только ссылка на элемент списка обобщающих параметров.

Итого:

  1. Есть два типа методов - обобщенные и не обобщенные.

  2. Обобщенные методы могут быть обобщенными явно и неявно.

  3. Приоритет при выборе перегруженного метода при прочих равных условиях:

    • не обобщенный
    • неявно обобщенный
    • явно обобщенный

В спецификации эту информацию найти можно, но только косвенно, прямых упоминаний нет, видимо понадеялись на логику читающих.

PS: Если есть другое объяснение, с удовольствием его прочитаю в вашем ответе.

READ ALSO
Запрос на сервер и выборка файла из базы

Запрос на сервер и выборка файла из базы

Привет изучаю сокеты на c# и для практики есть такая идеяЕсть два простеньких приложения сервер и клиент

372
Как сделать перемещение нод TreeView?

Как сделать перемещение нод TreeView?

Ветки нужно перемещать ноды на уровень выше в материнские и ниже в дочерние

395
Аргументы объекта

Аргументы объекта

Каким образом я могу вывести на экран аргументы моих объектов в методе cout?

460
Синхронизация 2-х потоков

Синхронизация 2-х потоков

Есть 2 потока , которые рисуют 2 машинки на консоли и имитируют их движениеМашинка -Враг- спускается сверху , а игрок должен ее обойти

377