Полиморфизм, перегрузки и дженерики в C # - PullRequest
12 голосов
/ 14 декабря 2011
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); }
    }

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

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

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

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

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

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

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

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

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

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

Ответы [ 5 ]

27 голосов
/ 14 декабря 2011

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

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

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

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

В чем различия между универсальными в C # и Java ... и шаблонами в C ++?

Подробности смотрите в длинных ответах.

См. Также мою статью на эту тему:

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

5 голосов
/ 14 декабря 2011

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

public static void Write<T>(T x) { System.Console.Write("{0}\n", x); }
3 голосов
/ 14 декабря 2011

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

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

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

1 голос
/ 14 декабря 2011

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

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

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

Обобщения C # и шаблоны C ++ не полностью эквивалентны.

0 голосов
/ 14 декабря 2011

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

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

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

return (int)(object) x;

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

...