Generics в Си#
Все правильно, у генериков именно разновидность компиляции перед выполнением. Как известно, компилятор С# на выходе имеет программу на CIL (Common Intermediate Language которая уже во время исполнения компилируется JIT (Just-In-Time) компилятором в коды машины, на которой она исполняется. Таким образом, для того, чтобы генерики стали возможны, в CIL добавили понятие generic type - класс, но с параметрами, другими типами (возможно тоже generic-овыми). В момент первого обращения к generic классу с конкретными параметрами, JIT-компилятор создает в памяти класс для данных параметров.
Но тогда чем это лучше (быстрее) Джавы? И вообще, почему многие считают, что Джава тормозная (так как интерпретируется а вот Си-шарп --- "нормальный компилируемый язык". Какой же он нах "нормальный", если его байт-код тоже требует докомпиляции...
Он не поэтому нормальным считается, а из-за решения детских болезней джавы.
Докомпиляция во-первых требует не слишком много времени, т.к. только один раз для одного куска кода выполняется, а тормозит обычно малая часть кода, которая выполняется многократно, т.е. относительные затраты на докомпиляцию получаются низкими, а, во-вторых, докомпиляцию можно выполнить во время инсталляции приложения, поскольку на этот момент уже известны все параметры машины (впрочем, при смене процессора придется перекомпилировать . В этом случае затраты на докомпиляцию нулевые.
Можно еще уточнить, что для различных параметров генерика, которые являются классами, используется одна реализация (т.е. один х86 код).
то в зависимости от T (ссылочный тип или value-type) в конструкторе будет выполняться два существенно различных действия - либо присвоение null, либо копирование в область памяти, отведенной под Value, нулей. Или там везде проверки стоят типа if (T is ValueType) {...} else{...}?
public class Lala<T>
{
public T Value = default(T);
}
Когда я говорил, что параметры являются классами, я имел в виду ссылочные типы. Для не ссылочных типов, для каждого набора параметров своя версия кода.
![](/images/graemlins/smile.gif)
Интересен факт, что эта оптимизация перестает работать вот в каком случае:
причем, у нас есть два-три разных value типа, которыми мы параметризуем этот генерик, причем, разные типа попадают на разные места примерно равномерно. Зачем нужен такой тип сразу не придумаю, но продолжим рассуждения из чувства прекрасного. Ведь у нас сгенерится 2^12-3^12 (4k-532k) реализаций кода.
public class Dozen<T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,T11,T12>
{
...
}
![](/images/graemlins/smile.gif)
![](/images/graemlins/smile.gif)
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 иметь параметры примитивного типа или только ссылки? Как генерится код по шаблону: так же, как и в C++ (то есть на этапе компиляции) или как в C# (то есть с докомпиляцией перед запуском или на лету или как-то иначе?
Начнём, пожалуй, новый тред
Да, в C# можно
то получится, что для кода под каждый конкретный тип будет отводиться по 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
Кто-нибудь, объясните на пальцах, что это за средство. Насколько я понимаю, похоже на шаблоны, т. е. проверка типов при компиляции. Но в то же время поддерживают динамический полиморфизм, т. е. позднее связывание.Не понимаю, как это сделано. Может быть, компиляция перед выполнением? Ведь подстановка шаблона должна делаться перед выполнением, чтобы обеспечить проверку типов...