Generics в Си#
Все правильно, у генериков именно разновидность компиляции перед выполнением. Как известно, компилятор С# на выходе имеет программу на CIL (Common Intermediate Language которая уже во время исполнения компилируется JIT (Just-In-Time) компилятором в коды машины, на которой она исполняется. Таким образом, для того, чтобы генерики стали возможны, в CIL добавили понятие generic type - класс, но с параметрами, другими типами (возможно тоже generic-овыми). В момент первого обращения к generic классу с конкретными параметрами, JIT-компилятор создает в памяти класс для данных параметров.
Я так и знал...
Но тогда чем это лучше (быстрее) Джавы? И вообще, почему многие считают, что Джава тормозная (так как интерпретируется а вот Си-шарп --- "нормальный компилируемый язык". Какой же он нах "нормальный", если его байт-код тоже требует докомпиляции...
Но тогда чем это лучше (быстрее) Джавы? И вообще, почему многие считают, что Джава тормозная (так как интерпретируется а вот Си-шарп --- "нормальный компилируемый язык". Какой же он нах "нормальный", если его байт-код тоже требует докомпиляции...
Он не поэтому нормальным считается, а из-за решения детских болезней джавы.
В Java пару лет назад тоже появился JIT-компилятор и она забегала раз в 5 быстрее. Сейчас ее скорость такая же, как и у .net, +/-, конечно.
Докомпиляция во-первых требует не слишком много времени, т.к. только один раз для одного куска кода выполняется, а тормозит обычно малая часть кода, которая выполняется многократно, т.е. относительные затраты на докомпиляцию получаются низкими, а, во-вторых, докомпиляцию можно выполнить во время инсталляции приложения, поскольку на этот момент уже известны все параметры машины (впрочем, при смене процессора придется перекомпилировать . В этом случае затраты на докомпиляцию нулевые.
Докомпиляция во-первых требует не слишком много времени, т.к. только один раз для одного куска кода выполняется, а тормозит обычно малая часть кода, которая выполняется многократно, т.е. относительные затраты на докомпиляцию получаются низкими, а, во-вторых, докомпиляцию можно выполнить во время инсталляции приложения, поскольку на этот момент уже известны все параметры машины (впрочем, при смене процессора придется перекомпилировать . В этом случае затраты на докомпиляцию нулевые.
Можно еще уточнить, что для различных параметров генерика, которые являются классами, используется одна реализация (т.е. один х86 код).
Хм, не знал. А как это достигается? Ведь если мы имеем вот такой класс:
то в зависимости от T (ссылочный тип или value-type) в конструкторе будет выполняться два существенно различных действия - либо присвоение null, либо копирование в область памяти, отведенной под Value, нулей. Или там везде проверки стоят типа if (T is ValueType) {...} else{...}?
public class Lala<T>
{
public T Value = default(T);
}
Когда я говорил, что параметры являются классами, я имел в виду ссылочные типы. Для не ссылочных типов, для каждого набора параметров своя версия кода.
Да, не обратил внимание. Спасибо, теперь понял. Разумно и клево. 
Интересен факт, что эта оптимизация перестает работать вот в каком случае:

Интересен факт, что эта оптимизация перестает работать вот в каком случае:
причем, у нас есть два-три разных value типа, которыми мы параметризуем этот генерик, причем, разные типа попадают на разные места примерно равномерно. Зачем нужен такой тип сразу не придумаю, но продолжим рассуждения из чувства прекрасного. Ведь у нас сгенерится 2^12-3^12 (4k-532k) реализаций кода.
public class Dozen<T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,T11,T12>
{
...
}
Только если они используются в программе. Страшная должно быть прога. Думаю, что такие еще писать не научились
Это примерно как страшилки про работу GC на x64 системах с большим кол-вом оперативки.
Это примерно как страшилки про работу GC на x64 системах с большим кол-вом оперативки.Согласен, это очередная паранойя 

public class Dozen<T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,T11,T12>Напомнило http://osl.iu.edu/~tveldhui/papers/2003/turing.pdf
Кстати, вопрос знатокам Java и C# --- в них такие программы, как в этой статье, как будут себя вести? Скомпилятся? Зависнут при компиляции? Зависнут при запуске?
Я вот пробовал на gcc компилировать программу с 5^17 степени шаблонами --- минут 15 он ее грыз, потихонечку отжирая память, потом мне надоело.
Когда я говорил, что параметры являются классами, я имел в виду ссылочные типы.А в C# можно нессылочные типы пихать в генерики? В жаве, вроде, нельзя, или я не прав?
Да, скажите, кто знает, как работают шаблоны в Java. Я знакомился с этим языком два года назад, тогда шаблонов не было.
Могут ли шаблоны Java иметь параметры примитивного типа или только ссылки? Как генерится код по шаблону: так же, как и в C++ (то есть на этапе компиляции) или как в C# (то есть с докомпиляцией перед запуском или на лету или как-то иначе?
Начнём, пожалуй, новый тред
Могут ли шаблоны Java иметь параметры примитивного типа или только ссылки? Как генерится код по шаблону: так же, как и в C++ (то есть на этапе компиляции) или как в C# (то есть с докомпиляцией перед запуском или на лету или как-то иначе?
Начнём, пожалуй, новый тред
Да, в C# можно
Я проверил. Ответ такой: скомпилируется все отлично. В рантайме, если не писать специального кода, который будет явно требовать использования всех нужных типов (т.к. jit-компиляция ленивая тоже все будет хорошо. Если же попробовать начать создавать кучу всяких типов, например, так:
то получится, что для кода под каждый конкретный тип будет отводиться по 4k, таким образом где-то на стотысячном типе все умрет из-за out of memory
public class Dozen<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12>
{
public static void Make
{
}
static Dozen
{
//Создаем конкретные типы асинхронно, иначе очень быстро вылетим по stack overflow
ThreadPool.QueueUserWorkItem(delegate(object state) { Dozen<T2, T1, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12>.Make; });
ThreadPool.QueueUserWorkItem(delegate(object state) { Dozen<T1, T3, T2, T4, T5, T6, T7, T8, T9, T10, T11, T12>.Make; });
ThreadPool.QueueUserWorkItem(delegate(object state) { Dozen<T1, T2, T4, T3, T5, T6, T7, T8, T9, T10, T11, T12>.Make; });
ThreadPool.QueueUserWorkItem(delegate(object state) { Dozen<T1, T2, T3, T5, T4, T6, T7, T8, T9, T10, T11, T12>.Make; });
ThreadPool.QueueUserWorkItem(delegate(object state) { Dozen<T1, T2, T3, T4, T6, T5, T7, T8, T9, T10, T11, T12>.Make; });
ThreadPool.QueueUserWorkItem(delegate(object state) { Dozen<T1, T2, T3, T4, T5, T7, T6, T8, T9, T10, T11, T12>.Make; });
ThreadPool.QueueUserWorkItem(delegate(object state) { Dozen<T1, T2, T3, T4, T5, T6, T8, T7, T9, T10, T11, T12>.Make; });
ThreadPool.QueueUserWorkItem(delegate(object state) { Dozen<T1, T2, T3, T4, T5, T6, T7, T9, T8, T10, T11, T12>.Make; });
ThreadPool.QueueUserWorkItem(delegate(object state) { Dozen<T1, T2, T3, T4, T5, T6, T7, T8, T10, T9, T11, T12>.Make; });
ThreadPool.QueueUserWorkItem(delegate(object state) { Dozen<T1, T2, T3, T4, T5, T6, T7, T8, T9, T11, T10, T12>.Make; });
ThreadPool.QueueUserWorkItem(delegate(object state) { Dozen<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T12, T11>.Make; });
ThreadPool.QueueUserWorkItem(delegate(object state) { Dozen<T12, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T1>.Make; });
}
}
public class Program
{
static void Main
{
Dozen<byte, sbyte, short, ushort, int, uint, long, ulong, float, double, TimeSpan, char>.Make;
Console.ReadLine;
}
}
Оставить комментарий
Missi4ka
Кто-нибудь, объясните на пальцах, что это за средство. Насколько я понимаю, похоже на шаблоны, т. е. проверка типов при компиляции. Но в то же время поддерживают динамический полиморфизм, т. е. позднее связывание.Не понимаю, как это сделано. Может быть, компиляция перед выполнением? Ведь подстановка шаблона должна делаться перед выполнением, чтобы обеспечить проверку типов...