Каковы различия между Дженериками в C# и Java … и Шаблоны в C++? [закрытый]

Я думаю, что использую mvc 5, и он работает для меня. System.Web.Mvc.dll версия 5.2.3.0

Я запускаю это в VS 2015, где используется c # 6.0. Таким образом, это либо то, либо версия 5.2.3.0, которая это делает.

Таким образом, ответ Кевина может быть не на 100% правильным.

MVC 6 не является «стабильным» в очень широкое понимание этого слова, поэтому мы используем MVC5 atm.

Обратите внимание, что сначала он не нашел мою статическую ссылку, но не было никаких раздражителей. После резки и повторной обработки строки «@using static» несколько раз она начала работать. Я могу перейти к определению в моем статическом методе расширения.

203
задан Shog9 11 April 2009 в 12:25
поделиться

13 ответов

Я добавлю свою речь к шуму и попробую ясно давать понять вещи:

Дженерики C# позволяют Вам объявлять что-то вроде этого.

List<Person> foo = new List<Person>();

и затем компилятор будет препятствовать тому, чтобы Вы поместили вещи, которые не являются Person в список.
Негласно компилятор C# просто помещает List<Person> в.NET dll файл, но во времени выполнения JIT-компилятор идет и создает новый набор кода, как будто Вы записали специальный класс списка только для содержания людей - что-то как ListOfPerson.

преимущество этого - то, что это делает его действительно быстро. Нет никакого кастинга или любого другого материала, и потому что dll содержит информацию, что это - Список Person, другой код, который смотрит на него, позже использование отражения может сказать, что содержит Person объекты (таким образом, Вы получаете intellisense и так далее).

оборотная сторона этого - то, что старый C# 1.0 и 1,1 кода (прежде чем они добавили дженерики) не понимают эти новые List<something>, таким образом, необходимо вручную преобразовать вещи назад в простой List для взаимодействия с ними. Это не является настолько большим из проблемы, потому что двоичный код C# 2.0 не назад совместим. Единственное время это будет когда-либо происходить, - то, при обновлении некоторого старого C# 1.0/1.1, код к дженерикам C# 2.0

Java позволяют Вам объявлять что-то вроде этого.

ArrayList<Person> foo = new ArrayList<Person>();

На поверхности это выглядит одинаково, и это, вид-. Компилятор будет также препятствовать тому, чтобы Вы поместили вещи, которые не являются Person в список.

различие - то, что происходит негласно. В отличие от C#, Java не идет и создает специальное предложение ListOfPerson - это просто использует простое ArrayList, который всегда был в Java. Когда Вы вытаскиваете вещи из массива, обычное Person p = (Person)foo.get(1);, танец кастинга все еще должен быть сделан. Компилятор сохраняет Вас нажатия клавиш, но хит/кастинг скорости все еще понесен точно так же, как это всегда было.
, Когда люди упоминают "Стирание Типа", это - то, о чем они говорят. Компилятор вводит броски для Вас, и затем 'стирает' то, что он предназначен, чтобы быть списком Person не всего Object

, преимущество этого подхода - то, что старый код, который не понимает дженериков, не должен заботиться. Это все еще имеет дело с то же старое ArrayList, как это всегда имеет. Это более важно в мире Java, потому что они хотели поддерживать код компиляции с помощью Java 5 с дженериками, и работая на нем старые 1.4 или предыдущая JVM, которой Microsoft сознательно решила не обеспокоиться.

оборотная сторона является хитом скорости, который я упомянул ранее, и также потому что нет никакого ListOfPerson псевдокласс или что-либо как этот вход в .class файлы, кодируйте, который смотрит на него позже (с отражением, или если Вы вытаскиваете его из другого набора, где это было преобразовано в [1 121], или так на) не может сказать всегда, что это предназначено, чтобы быть списком, содержащим [только 1 122] и не только любой другой список массива.

Шаблоны C++ позволяют Вам объявлять что-то вроде этого

std::list<Person>* foo = new std::list<Person>();

, Это похоже на C# и дженерики Java, и это сделает то, что Вы думаете, что это должно сделать, но негласно разные вещи происходят.

Это имеет большинство вместе с дженериками C#, в которых это создает особенный pseudo-classes вместо того, чтобы просто выбросить информацию о типе как Java, делает, но это - целое другое дело.

И C# и Java производят вывод, который разработан для виртуальных машин. Если Вы запишете некоторый код, который имеет Person класс в нем, в обоих случаях некоторая информация о Person, то класс войдет в .dll или .class файл, и JVM/CLR сделает материал с этим.

C++ производит сырые данные x86 двоичный код. Все не объект, и нет никакой базовой виртуальной машины, которая должна знать о Person класс. Нет никакой упаковки или распаковывания, и функции не должны принадлежать классам, или действительно чему-либо.

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

В C# и Java, система дженериков должна знать, какие методы доступны для класса, и это должно передать это к виртуальной машине. Единственный способ сказать его это или жестким кодированием фактический класс в или использующими интерфейсами. Например:

string addNames<T>( T first, T second ) { return first.Name() + second.Name(); }

, Что код не скомпилирует в C# или Java, потому что это не знает, что тип T на самом деле предоставляет метод под названием Имя (). Необходимо сказать его - в C# как это:

interface IHasName{ string Name(); };
string addNames<T>( T first, T second ) where T : IHasName { .... }

И затем необходимо удостовериться вещи, Вы передаете реализации addNames интерфейс IHasName и так далее. Синтаксис Java отличающийся (<T extends IHasName>), но он страдает от тех же проблем.

'классический' случай для этой проблемы пытается записать функцию, которая делает это

string addNames<T>( T first, T second ) { return first + second; }

, Вы не можете на самом деле записать этот код, потому что нет никаких способов объявить интерфейс с + метод в нем. Вы перестали работать.

C++ не страдает ни от одной из этих проблем. Компилятор не заботится о передаче типов ни к какому VM's - если оба Ваших объекта будут иметь.Name () функция, это скомпилирует. Если они не сделают, это не будет. Простой.

Так, там у Вас есть он:-)

364
ответ дан Orion Edwards 23 November 2019 в 04:56
поделиться

Похож, среди других очень интересных предложений, существует один о совершенствовании дженериков и повреждении назад совместимость:

В настоящее время, дженерики реализованы с помощью стирания, что означает, что универсальная информация о типе не доступна во времени выполнения, которое делает некоторый код трудно для записи. Дженерики были реализованы этот способ поддерживать назад совместимость с более старым неуниверсальным кодом. Овеществленные дженерики сделали бы универсальную информацию о типе доступной во времени выполнения, которое повредит неуниверсальный код прежней версии. Однако Neal Gafter предложил делать типы reifiable, только если указанный, чтобы не повредить обратную совместимость.

в статья Alex Miller о Java 7 Предложений

1
ответ дан Pontus Gagge 23 November 2019 в 04:56
поделиться

В Java дженерики являются уровнем компилятора только, таким образом, Вы добираетесь:

a = new ArrayList<String>()
a.getClass() => ArrayList

Примечание, что тип списка массива, не списка строк. Таким образом, тип списка бананов равнялся бы () списку обезьян.

Так сказать.

1
ответ дан izb 23 November 2019 в 04:56
поделиться

Шаблоны C++ на самом деле намного более мощны, чем их C# и дубликаты Java, поскольку они оценены во время компиляции и поддерживают специализацию. Это допускает Шаблонное Метапрограммирование и делает компилятор C++ эквивалентным Машине Тьюринга (т.е. во время процесса компиляции можно вычислить что-либо, что вычислимо с Машиной Тьюринга).

1
ответ дан On Freund 23 November 2019 в 04:56
поделиться

Википедия имеет большие рецензии, выдерживающие сравнение и дженерики Java/C# и Дженерики/C++ Java шаблоны. основная статья о Дженериках кажется немного нарушенной, но она действительно имеет некоторую хорошую информацию в нем.

2
ответ дан travis 23 November 2019 в 04:56
поделиться

Самая большая жалоба является стиранием типа. В этом дженерики не осуществляются во времени выполнения. Вот ссылка на некоторые документы Sun о предмете .

Дженерики реализованы стиранием типа: универсальная информация о типе присутствует только во время компиляции, после которого она стирается компилятором.

1
ответ дан Matt Cummings 23 November 2019 в 04:56
поделиться

И Java и C# представили дженерики после их первого выпуска языка. Однако существуют различия в том, как оперативные библиотеки изменились, когда дженерики были представлены. дженерики C# не являются просто волшебством компилятора и таким образом, это не было возможно к generify существующие классы библиотеки, не повреждая назад совместимость.

, Например, в Java существующее Платформа Наборов была полностью genericised. Java не имеет и универсальной и неуниверсальной версии прежней версии классов наборов. До некоторой степени это намного более чисто - если необходимо использовать набор в C# существует действительно очень мало причины пойти с неуниверсальной версией, но те классы прежней версии остаются на месте, загромождая среду.

Другими заметными различиями являются Классы Enum в Java и C#. Перечисление Java имеет это несколько извилисто выглядящее определение:

//  java.lang.Enum Definition in Java
public abstract class Enum<E extends Enum<E>> implements Comparable<E>, Serializable {

(см. Angelika Langer, очень ясную объяснение точно, почему это так. По существу это означает, что Java может предоставить безопасный с точки зрения типов доступ от строки до ее Перечисления значений:

//  Parsing String to Enum in Java
Colour colour = Colour.valueOf("RED");

Сравнивают это с версией C#:

//  Parsing String to Enum in C#
Colour colour = (Colour)Enum.Parse(typeof(Colour), "RED");

, Поскольку Перечисление уже существовало в C#, прежде чем дженерики были представлены языку, определение не могло измениться, не повреждая существующий код. Так, как наборы, это остается в оперативных библиотеках в этом состоянии прежней версии.

6
ответ дан Adam Bellaire 23 November 2019 в 04:56
поделиться

Продолжение моей предыдущей регистрации.

Шаблоны являются одной из главных причин, почему C++ перестал работать так плачевно в intellisense, независимо от используемого IDE. Из-за шаблонной специализации IDE никогда не может быть действительно уверен, существует ли данный участник или нет. Рассмотрите:

template <typename T>
struct X {
    void foo() { }
};

template <>
struct X<int> { };

typedef int my_int_type;

X<my_int_type> a;
a.|

Теперь, курсор в обозначенном положении, и это чертовски трудно для IDE для высказывания в той точке, если, и что, имеют участники a. Для других языков парсинг был бы прост, но для C++, довольно мало оценки необходима заранее.

Это ухудшается. Что, если my_int_type были определены в шаблоне класса также? Теперь его тип зависел бы от другого аргумента типа. И здесь, даже сбой компиляторов.

template <typename T>
struct Y {
    typedef T my_type;
};

X<Y<int>::my_type> b;

После небольшого количества взглядов, программист пришел бы к заключению, что этот код совпадает с вышеупомянутым: Y<int>::my_type твердость к int, поэтому b должна быть тем же типом как a, правильно?

Неправильно. В точке, где компилятор пытается разрешить этот оператор, он еще на самом деле не знает Y<int>::my_type! Поэтому это не знает, что это - тип. Это могло быть что-то еще, например, функция членства или поле. Это могло бы дать начало неоднозначностям (хотя не в данном случае), поэтому компилятор перестал работать. Мы должны сказать ему явно, что обращаемся к имени типа:

X<typename Y<int>::my_type> b;

Теперь, компиляции кода. Чтобы видеть, как неоднозначности являются результатом этой ситуации, рассмотрите следующий код:

Y<int>::my_type(123);

Этот оператор кода совершенно допустим и говорит C++ выполнять вызов функции к [1 111]. Однако, если бы my_type не функция, а скорее тип, этот оператор все еще был бы допустим и выполнил бы специальный бросок (бросок функционального стиля), который часто является вызовом конструктора. Компилятор не может сказать, который мы имеем в виду, таким образом, мы должны снять неоднозначность здесь.

14
ответ дан Konrad Rudolph 23 November 2019 в 04:56
поделиться

Уже существует много хороших ответов на , каковы различия, таким образом позвольте мне дать немного отличающуюся перспективу и добавьте почему .

, Как был уже объяснен, основное различие стирание типа , т.е. то, что компилятор Java стирает универсальные типы и они не заканчивают в сгенерированном байт-коде. Однако вопрос: почему кто-либо сделал бы это? Это не имеет смысла! Или он?

ну, какова альтернатива? Если Вы не реализуете дженериков на языке, где делают , Вы реализуете их? И ответ: в Виртуальной машине. Который повреждает назад совместимость.

стирание Типа, с другой стороны, позволяет Вам смешивать универсальные клиенты с неуниверсальными библиотеками. Другими словами: код, который был скомпилирован на Java 5, может все еще быть развернут на Java 1.4.

Microsoft, однако, решила повредить назад совместимость для дженериков. Это , почему Дженерики.NET "лучше", чем Дженерики Java.

, Конечно, Sun не является идиотами или трусами. Причина, почему они "пошли на попятный", состояла в том, что Java был значительно более старым и более широко распространенным, чем.NET, когда они представили дженерики. (Они были представлены примерно одновременно в обоих мирах.) Повреждающий назад совместимость была бы огромная боль.

Помещенный еще один путь: в Java Дженерики являются частью Язык (что означает, что они применяются [только 116] к Java, не к другим языкам), в.NET, они - часть Виртуальная машина (что означает, что они относятся весь языки, не только C# и Визуальный Basic.NET).

Сравнивают это с функциями.NET как LINQ, лямбда-выражения, вывод типа локальных переменных, анонимные типы и деревья выражений: они - весь язык функции. Вот почему существуют тонкие различия между VB.NET и C#: если бы теми функциями была часть VM, они были бы тем же в [1 110] весь языки. Но CLR не изменился: это - все еще то же в.NET 3,5 SP1, как это было в.NET 2.0. Можно скомпилировать программу C#, которая использует LINQ с.NET 3,5 компилятора и все еще выполняет его на.NET 2.0, при условии, что Вы не используете.NET 3,5 библиотеки. Это было бы не работа с дженериками и.NET 1.1, но это будет работа с Java и Java 1.4.

18
ответ дан Jörg W Mittag 23 November 2019 в 04:56
поделиться

Сам Anders Hejlsberg описал различия здесь" Дженерики в C#, Java и C++ ".

35
ответ дан jfs 23 November 2019 в 04:56
поделиться

C++ редко использует “generics” терминологию. Вместо этого слово “templates” используется и более точно. Templates описывает один техника для достижения универсального дизайна.

C++ обрабатывает по шаблону, очень отличается от того, что и C# и Java реализуют по двум главным причинам. Первая причина состоит в том, что шаблоны C++ не только позволяют аргументы типа времени компиляции, но также и аргументы значения константы времени компиляции: шаблоны могут быть даны как подписи четной функции или целые числа. Это означает, что можно сделать некоторый довольно броский материал во время компиляции, например, вычисления:

template <unsigned int N>
struct product {
    static unsigned int const VALUE = N * product<N - 1>::VALUE;
};

template <>
struct product<1> {
    static unsigned int const VALUE = 1;
};

// Usage:
unsigned int const p5 = product<5>::VALUE;

Этот код также использует другую выдающуюся функцию шаблонов C++, а именно, обработайте специализацию по шаблону. Код определяет один шаблон класса, product, который имеет один аргумент значения. Это также определяет специализацию для того шаблона, который используется каждый раз, когда аргумент оценивает к 1. Это позволяет мне определять рекурсию по шаблонным определениям. Я полагаю, что это было сначала обнаружено Andrei Alexandrescu .

Шаблонная специализация важна для C++, потому что это допускает структурные различия в структурах данных. Templates в целом является средством объединения интерфейса через типы. Однако, хотя это желательно, все типы нельзя рассматривать одинаково в реализации. Шаблоны C++ принимают это во внимание. Это - почти такое же значение, которое ООП имеет между интерфейсом и реализацией с переопределением виртуальных методов.

шаблоны C++ важны для его алгоритмической парадигмы программирования. Например, почти все алгоритмы для контейнеров определяются как функции, которые принимают контейнерный тип как шаблонный тип и рассматривают их однородно. На самом деле это не совсем правильно: C++ не работает над контейнерами, а скорее над диапазоны , которые определяются двумя итераторами, указывая на начало и позади конца контейнера. Таким образом целое содержание ограничено итераторами: начните < = элементы < конец.

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

Другим отличительным признаком C++ является возможность частичная специализация для шаблонов классов. Это несколько связано с сопоставлением с образцом на аргументах в Haskell и других функциональных языках. Например, давайте рассмотрим класс, который хранит элементы:

template <typename T>
class Store { … }; // (1)

Это работает на любой тип элемента. Но скажем, что мы можем сохранить указатели больше effciently, чем другие типы путем применения некоторого специального приема. Мы можем сделать это частично специализация для всех типов указателей:

template <typename T>
class Store<T*> { … }; // (2)

Теперь, каждый раз, когда мы инстанцируем контейнерный шаблон для одного типа, соответствующее определение используется:

Store<int> x; // Uses (1)
Store<int*> y; // Uses (2)
Store<string**> z; // Uses (2), with T = string*.
61
ответ дан Mankarse 23 November 2019 в 04:56
поделиться

11 months late, but I think this question is ready for some Java Wildcard stuff.

This is a syntactical feature of Java. Suppose you have a method:

public <T> void Foo(Collection<T> thing)

And suppose you don't need to refer to the type T in the method body. You're declaring a name T and then only using it once, so why should you have to think of a name for it? Instead, you can write:

public void Foo(Collection<?> thing)

The question-mark asks the the compiler to pretend that you declared a normal named type parameter that only needs to appear once in that spot.

There's nothing you can do with wildcards that you can't also do with a named type parameter (which is how these things are always done in C++ and C#).

4
ответ дан 23 November 2019 в 04:56
поделиться

NB: У меня недостаточно точки, чтобы комментарий, поэтому не стесняйтесь переместить его как комментарий к соответствующему ответу.

Вопреки распространенному мнению, откуда я никогда не понимаю, .net реализовал настоящие дженерики без нарушения обратной совместимости, и они приложили для этого явные усилия. Вам не нужно преобразовывать свой неуниверсальный код .net 1.0 в универсальный код только для использования в .net 2.0. И общие, и неуниверсальные списки по-прежнему доступны в .Net framework 2.0 даже до версии 4.0, только по причине обратной совместимости. Поэтому старые коды, которые все еще использовали неуниверсальный ArrayList, по-прежнему будут работать и будут использовать тот же класс ArrayList, что и раньше. Обратная совместимость кода всегда поддерживается с 1.0 до настоящего времени ... Таким образом, даже в .net 4.0 вам все равно придется использовать любой неуниверсальный класс из 1.0 BCL, если вы решите это сделать.

Поэтому я не думаю, что Java должна нарушать обратную совместимость, чтобы поддерживать истинные дженерики.

0
ответ дан 23 November 2019 в 04:56
поделиться