Подтвердить что ты не робот

Полиморфизм, перегрузки и дженерики в С#

class Poly
    {
    public static void WriteVal(int i) { System.Console.Write("{0}\n", i); }
    public static void WriteVal(string s) { System.Console.Write("{0}\n", s); }
    }

class GenWriter<T>
    {
        public static void Write(T x) { Poly.WriteVal(x); }
    }

Почему метод невинных (для С++-программистов) Write не подходит для С#?

Вы можете видеть, что компилятор пытается сопоставить тип параметра T с конкретными перегрузками before:

Ошибка 3 Наилучшее перегруженное соответствие метода для 'TestGenericPolyMorph.Poly.WriteVal(int)' имеет некоторые недопустимые аргументы

Конечно. целью было не использовать статический метод, как указано выше, цель состоит в создании оболочки с полиморфным поведением. Примечание. Я использую VS 2010.

Обратите внимание, что вся необходимая информация доступна во время компиляции. Еще раз: проблема в том, что проверка выполняется до создания шаблона.

Добавление после обсуждения:

Ну, может быть, я не особо подчеркивал это. Речь шла не только о различии между дженериками и шаблонами, но и о решении следующей проблемы: данный набор перегрузок, адресующих разные типы, я хочу создать набор классов-оболочек, предоставляющих виртуальный метод (полиморфизм) для этих типов. Цена разрешения виртуальных методов во время выполнения минимальна и не влияет на производительность. Именно здесь были удобны шаблоны С++. Очевидно, что накладные расходы на разрешение типа рабочего времени для динамического существенно отличаются. Итак, вопрос заключается в том, можно ли преобразовать существующие перегрузки в полиморфизм без репликации кода и без уплаты штрафа за производительность (например, я не уверен, что я получаю с помощью динамического по сравнению с "переключателем", пытающимся вместо отличного синтаксиса).

Одним из решений, которые я видел до сих пор, было создание/испускание кода (sic!), то есть вместо вырезания и вставки, чтобы сделать это автоматически.

Итак, вместо обработки шаблонов С++ мы просто делаем это вручную или просто заново изобретаем макро/шаблонный процессор.

Что-нибудь лучше?

4b9b3361

Ответ 1

Короткий ответ:

Генераторы С# не являются шаблонами С++; несмотря на их аналогичный синтаксис, они совершенно разные. Шаблоны создаются во время компиляции, один раз за экземпляр, а шаблонный код должен быть правильным только для фактических аргументов шаблона. Шаблоны выполняют задачи, такие как разрешение перегрузки и анализ типа один раз за экземпляр; они в основном являются умным механизмом поиска и замены текста исходного кода.

Генераторы С# - это действительно общие типы; они должны быть правильными для любого возможного аргумента типа. Общий код анализируется один раз, разрешение перегрузки выполняется один раз и т.д.

Длинный ответ: это дубликат

Каковы различия между Generics в С# и Java... и шаблонами в С++?

Подробнее см. подробные ответы.

См. также мою статью по теме:

http://blogs.msdn.com/b/ericlippert/archive/2009/07/30/generics-are-not-templates.aspx

Ответ 2

Почему вы не можете просто написать:

public static void Write<T>(T x) { System.Console.Write("{0}\n", x); }

Ответ 3

Генерирование С++ и С# отличается (http://msdn.microsoft.com/en-us/library/c6cyy67b(v=VS.80).aspx, ищите "разницу общих символов С# С++" на вашем любимом сайте поиска)

Коротко: компилятор С# должен создать полный класс GenWriter<T> со всеми типами соответствия, просто посмотрев на сам класс. Поэтому он не знает, будет ли T только int/string или любым другим типом.

Компилятор С++ создает фактический класс, рассматривая создание универсального GenWriter<int> и объявления GenWriter<T>, а затем создает класс для этого конкретного экземпляра.

Ответ 4

Если кто-то должен был вызвать GenWriter(5.0), это будет выведено на GenWriter<double>(5.0), а вызов метода внутри Write(T x) станет следующим:

public static void Write(double x) { Poly.WriteVal(x); }

Нет перегрузки WriteVal, которая принимает двойной. Компилятор сообщает вам, что нет допустимых перегрузок WriteVal.

Генерирование С# и шаблоны С++ не совсем эквивалентны.

Ответ 5

Вы не можете сделать это на С#, потому что компилятор не знает, что такое тип x во время компиляции.

Без знания фактического типа T, компилятор обеспокоен тем, что вы, возможно, намеревались выполнить пользовательское преобразование. Самое простое решение - использовать оператор as, который не имеет аналогов, поскольку он не может выполнить пользовательское преобразование.

Более геральное решение состоит в том, чтобы сначала сбрасывать объект. Это полезно из-за проблем с распаковкой бокса:

return (int)(object) x;

Имейте в виду, что С# Generics не похожи на шаблоны С++. Шаблоны С++ представляют собой фрагменты кода, которые компилируются для каждого типа отдельно. Хотя генераторы С# скомпилированы в assebmly.