Страницы

Поиск по вопросам

суббота, 30 ноября 2019 г.

Зачем нужен 0xDEADBEEF?

#c #c++


Я разбираюсь в программе и вижу объявление
unsigned long transfer[TRANSFER_LENGTH] = { 0xDEADBEEF };

а ниже по коду
for (int i = 0; i < TRANSFER_LENGTH; i++)
    transfer[i] = GetData();

Вот зачем присваивать transfer 0xDEADBEEF, а не взять просто 0?    


Ответы

Ответ 1



Потому что DEADBEEF прекрасно видно в hex-dump'е как осмысленная константа 0xDEADBEEF. Вот представьте себе, что Вы анализируете лог обмена и видите там 00000000. Неясно - была передача данных или нет. И что нули означают. А когда получается DEADBEEF понятно, что что-то произошло нехорошее и в каком конкретно месте нужно искать причину. Еще раз поясню, что при нормальном исполнении программы вероятностью увидеть DEADBEEF (или любую другую аналогичную) константу равна 0. А нолей в памяти бывает сколько угодно. Вот на этом игра и ведется. PS: еще обращу внимание на два момента: DEADBEEF по-английски означает 'дохлое мясо' :-) константа DEADBEEF одинаково записывается как строчка и как байты в hex-представлении

Многоязычность Интернет страницы/сайта

#apache #htaccess #php


Стало интересно как можно реализовать многоязычность Интернет страницы/сайта, "погуглил"
некоторое время, но на толковое разъяснение что да как не наткнулся... Не могли бы
Вы товарищи ткнуть носом в строну куда надо копать?    


Ответы

Ответ 1



Нужно сделать файл, содержащий объявление констант, в которых будут строки для вывода в разных местах сайта. Например (приводил уже где то)... Главный файл: Файл русского языка: Файл английского языка: Файл шаблона: <?=LANG_TITLE?>

Просто нужно поместить в $language имя файла языка, для удобства без расширения. Следует еще предусмотреть вывод страницы на дефолтном языке, если в $language ничего не присвоено. Вот, такова идея. Если что не ясно, пишите! Удачного дня :)

Ответ 2



Процесс создания мультиязычности обычно делят на две части: интернационализацию и локализацию. Оба этих процесса хорошо описаны в wikipedia. Наиболее популярным и удобным инструментом для интернационализации, как сайтов, так и прикладных программ является gettext. Для PHP так же есть соответствующее расширение. Основная идея этой библиотеки заключается в том, что для обозначения переводимой используется сама оригинальная строка, а не какие-либо специальные идентификаторы. При этом, если перевод данной строки отсутствует, то просто выводится оригинальная строка. Например, тот же шаблон ответа выше будет выглядеть так: <?= _('Main page') ?>

Более подробно про gettext и его возможностях можно прочитать в той-же wikipedia. PS В документации к gettext говорится, что оригинальные строки должны быть на английском, однако, в последних версиях gettext можно использовать и русские строки, при условии повсеместного использования utf-8. В этом случае, возможно, будет некоторая путаница в английской локализации, но это проблема легко решается.

Ответ 3



Хотелось бы дополнить, если использовать базу данных, то в списке страниц сайта, можно добавить вариант текста на нескольких языках пример таблицы : id - идентификатор страницы в базе name - имя на английском как псевдоним t_ru - название на русском t_en - название на английском c_ru - контент на русском c_en - контент на английском и при необходимости грузить только нужную колонку, язык давать на выбор изначально, а сохранять его параметры в cookie, например :
Примерно как то так, способов реализаций очень много, в большинстве случаях можно обойтись и без базы данных и использовать файлы. Весь смысл создания сайта на разных языках - это знать нужный для посетителя язык и выводить вариант страницы на этом языке. А если нужно перевести надписи на кнопках, заголовки и т.д., то можно создать файл с массивами, и подключать его в начале страницы функцией require, именно ей и именно вначале, т.к. она не замедляет работу программы, как это делает include();. пример : '; ?> Сразу извиняюсь за возможные ошибки, т.к. пишу без возможности проверить.

Использование Си в C++ программах : все за и против [закрыт]

#c #c++ #стиль


Нужно ли воспринимать возможность использовать Си в программах на С++, как приятное
дополнение или относится только как к обратной совместимости?

Есть те, кто считает, что такое использование вредит пониманию, так как другой человек
может быть не знаком с некоторыми частями Си или вовсе не знать ничего, кроме С++.
Также некоторые считают это плохим стилем, потому что его использование делает код
не красивым. Или же с точки зрения убеждений, что код должен содержать конструкции
языка, на котором пишешь, а не смешивание new с malloc т.п.
Но а как же быстродействие некоторых его частей или использование удобных функций
форматирования?
Где та грань, которой нужно придерживаться?

Я описал только малую часть, так как на большее не хватает знаний. По этому было
бы прекрасно, если бы вы ответили не точно следуя этим вопросам, а опираясь на свой
опыт и мнение и этим возможно тема раскроется ещё глубже, что в дальнейшем поможет
и мне и возможно вам или кому-то другому.
UPD:
@avp, на материал во второй ссылке буду постепенно поглядывать, но скорей для справки,
чем искать повод для переезда на другой язык. Потому как в принципе я согласен со многим,
даже при том, что у меня очень мало опыта. Мне кажется, что если начинать с С++, то
это скорей будет изучение самого С++, а потом уже возможно через пару лет и программирования,
что нельзя сказать про Си, так как он действительно прост в понимании, особенно после
С++. И концентрация идет не на разбирание граблей языка или умения работать с stl/boost,
а на освоение новых алгоритмов при написании своих костылей. Такие костыли будут в
начале плохими и некрасивыми и на них будет уходить много времени в сравнении с использованием
готовых решений на C++, но их написание даст понимание как это работает или как это
выгодно модифицировать для своих нужд, а не просто использовать STL и даже может разбираться
частично как работают его внутренности, но все ровно не быть способным написать что-то
подобное на том же языке или другом.

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

Наверное я бы выбрал Си, если бы вернулся в прошлое, но сейчас буду и дальше продолжать
кодить с упором на С++ и поглядывать на С, то есть бежать наверх в низ по ступенькам.
Потому что во мне преобладает непостоянность и я просто обязан закончить с этой книгой).
Вот такие пироги.

P.S. Спасибо за ответы и приношу извинения, если развел здесь оффтоп.    


Ответы

Ответ 1



На самом деле, единой точки зрения на это нету. С одной стороны, С — очень мощное подмножество языка, и используя его, можно легко «прострелить себе ногу», то есть, наделать глупых ошибок по незнанию. Например, при помощи макросов можно сделать как много полезного, так и много вредного наподобие #define strcpy(a,b) memmove(a,b,strlen(b)+2), поэтому во многих проектах макросов стараются избегать — тем более, что при помощи гораздо более безопасных темплейтов можно сделать многое из того, что умеют макросы (и кроме того многое другое). Другими частыми источниками ошибок являются смешивание malloc/free и new/delete/delete[] (по стандарту, free(new int) есть undefined behaviour), ручное управление памятью вместо использования смарт-указателей и RAII (источник 90% проблем новичков на ХК), использование массивов (и более продвинутое велосипедостроительство) вместо подходящих стандартных контейнеров std::vector, std::map, std::stack (источник остальных 90% проблем новичков). Особенно сложно даётся новичкам работа со строками (которая, нужно признать, в C организовано не блестяще): немногие осилят правильное чтение строки не известного заранее размера из файла! С другой стороны, многие очень приятные лаконичные конструкции C не имеют прямого «безопасного» C++-варианта, например, обсуждавшийся недавно sscanf. Не стоит забывать, что C — другой язык, с другой идеологией, методологией и эстетикой, поэтому смесь кода на двух языках выглядит ненатурально и является источником ошибок. Например, в C приветствуются лаконичные конструкции: void copy_str(char *p, char *q) { while (*q++ = *p++) ; } — в то время как в рамках C++ этот код слишком плотный, чересчур завязан на конкретные типы, слишком прямо работает с памятью и и слишком надеется на непроверяемые предусловия (априори считается, что q указывает на выделенный кусок памяти размером не менее strlen(p) + 1). Для себя я установил правило: если пишется код на C++, стараться использовать идиоматичные конструкции, чтобы тем, кто читает мой код, было легче понимать его, и чтобы уменьшить вероятность ошибки: если выбран язык C++, а не C, надёжность более важна, чем экономия нескольких тактов процессора. Если же в каком-то месте я вижу необходимость использовать конструкцию из C, я пишу комментарий, обосновывающий эту необходимость. В любом случае, присоединяясь к существующему проекту на C++, узнайте, какие там стандарты на использование конструкций чистого C, и следуйте им. Update (спасибо @alexlz за комментарий!) Заметьте, что в случае ограниченности ресурсов (программирование для контроллеров, например), использование «полноценного» C++ может быть неоправданно затратным. Это усугубляется тем, что компиляторы для таких платформ зачастую выдают плохо оптимизированный код. В таких случаях распространённой практикой является отказ от всего множества языковых фич C++ и ограничение определённым подмножеством. Например, могут быть выброшены лямбды, шаблоны, стандартные контейнеры, исключения, стандартные алгоритмы, введено ручное управление памятью, то есть, итоговый язык будет ближе к чистому C. Но это — не от хорошей жизни (высокоуровневые конструкции позволяют забыть о надоедливых мелочах), и определяется опытным в разработке на данной платформе архитектором проекта.

Ответ 2



@strol, программирование само по себе достаточно сложная штука. Язык должен помогать как можно проще и естественней выражать свои мысли. На самом деле С++ язык сложный (а ведь создавался с целью упростить программирование на Си). Поэтому старайтесь писать проще (однако, не проще, чем необходимо) и не слишком переживайте по поводу смешения стилей. Конечно, некоторые вещи, которые на первый взгляд эквивалентны, могут на самом деле оказаться несовместимыми (например, new/delete и malloc/free). Просто о них надо знать, т.е. понимать как реализуются разные конструкции. В самом С++ полно взаимозависимых вещей и добавление чего-то из чистого Си ситуацию не упрощает (но и далеко не всегда усложняет). Примерно на эту тему мне понравилось высказывание Кернигана (кстати, они работали с Страуструпом в Bell labs) в интервью из книги "Пионеры программирования" ...Си занимает прочную позицию среди языков программирования. Он чрезвычайно выразителен, но в то же время не слишком сложен или велик, а кроме того, написанные на нем программы эффективны. ... С этим языком удобно работать, потому что если нужно что-то выразить, он представляет для этого не так много разных способов. Я посмотрю на ваш код и скажу: да, мне понятно, что он делает. Едва ли то же самое можно сказать о таких языках, как Perl или С++. Я посмотрю на ваш код и останусь в недоумении, потому что тут есть много способов написать одно и то же. C++ сложен и огромен, и выразить что-либо можно многими способами. Если мы с вами будем писать на C++, то можем прийти к весьма разным способам описания какой-нибудь большой задачи. В Си такого не бывает. Си сохранился потому, что у него оказалось хорошее соотношение выразительности и эффективности, и для важных приложений он остается лучшим инструментом. .... Бьерн голову себе сломал, пытаясь добиться максимальной совместимости с Си. Одной из причин успеха C++ в сравнении с другими языками была хорошая совместимость на уровне как исходного, так и объектного кода, а это означало отсутствие необходимости полностью перестраивать работу, чтобы использовать C++ в среде Си. .... Одним из крупнейших прегрешений считают чрезмерную близость к Си.... ... Возможно, но чем дальше он отошел бы от Си, тем меньше были бы его шансы на успех. Здесь трудно соблюсти правильную меру, и я думаю, что он очень хорошо справился со своей задачей. (приношу извинения за столь длинную цитату) А некоторые люди вообще высказываются о программировании на С++ довольно кратко, но значительно резче. Здесь весьма обстоятельная критика. Так что, изучайте все получше и делайте выводы сами для себя.

Ответ 3



ИМХО, основное достоинство С - возможность написания быстрых программ. А если не быстрых, то совместимых/работающих с быстрыми. С++ - это С с немного более человеческим лицом. Но основная идея - по-прежнему скорость. Допустим, в std::list вы не найдёте метода sort. Хотя, конечно, есть дополна простых, но неэффективных методов типа std::find или insert в vector... Ну что поделаешь, какую-то цену за удобство надо платить. Это всё я к тому, что я считаю возможность смешивания С++ и С стилей одним из главных достоинств языка. Ну лень тебе сегодня - завёл vector. А завтра оказалось, что это всё тормозит - заменил vector на vector, память выделил один раз на все строки, раздал и всё! Я даже память для массивов частенько выделяю через std::string m_memory; ... m_memory.resize(data_size); return m_memory.c_str() Как ни странно, ни разу сильно не огребал из-за подобных шалостей. Гораздо тяжелей было научиться работать со строками без access violation'ов в С, когда только начинал :) Upd: @VladD, ну естественно не на стеке! Имеется в виду член класса, который живёт дольше, чем используется память, которую он держит. Я потому и написал m_: class C { private: std::string m_memory; ... char *GetWorkMemory(int data_zize) { m_memory.resize(data_size); return m_memory.c_str(); } ...

Могу ли я сделать вывод программы на Python цветным?

#python #color #windows


Хочу например чтоб строки вида, а точнее их вывод в консоль:
print 'Hello World'

Были зеленого цвета. Это возможно? Подскажите как?    


Ответы

Ответ 1



Используются ANSCII escape symbols, некоторые из них отвечают за цвета. В большинстве терминалов Linux они поддерживаются. Можно просто погуглить на эту тему и не обязательно скачивать colorama, хотя, наверняка, это упростит работу. Просто проверьте и всё. import sys import time import random string = "123456789\n" def color(text): sys.stdout.write(u"\x1B[{0}m{1}\x1B[0m".format (random.choice(range(31,36)+ range(90,97)),i)) sys.stdout.flush() time.sleep(random.random()/6) for i in string: color(i)

Ответ 2



colorama Cross-platform colored terminal text.

Ответ 3



https://pypi.python.org/pypi/termcolor попробуй библиотеку;)И в использовании легче from termcolor import colored print(colored('Hello World!', 'green')

Ответ 4



Чтобы не вводить руками ANSI коды для цветов и смены текущей позиции, blessings пакет может быть использован: from blessings import Terminal # $ pip install blessings import colorama # $ pip install colorama colorama.init() # replace ANSI escapes with Win32 calls in stdout/stderr t = Terminal() with t.location(0, t.height - 1): # at the bottom of the terminal print(t.green('Hello World')) colorama, предложенная @Ilya Pirogov, позволяет одному и тому же коду работать как на Linux, OS X, других Unices так и Windows.

Как отменить git init в уже существующем репозитории?

#git #git_init


В папке с git-ом случайно нажал на git init , в итоге теперь когда делаю git status
у меня все файлы отображаются как измененные.

В git log последние коммиты остались. Как отменить действие команды git init?
    


Ответы

Ответ 1



Если просто случайно создали репозиторий, то нужно удалить папку .git в корне. Это полностью уничтожит репозиторий и, разумеется, отменит то, что сделал git init. Через *nix-консоль это делается так: rm -r .git Если же Вы сделали git init в уже существующем репозитории, то бояться нечего: Running git init in an existing repository is safe. It will not overwrite things that are already there. The primary reason for rerunning git init is to pick up newly added templates (or to move the repository to another place if --separate-git-dir is given).

Ответ 2



Судя по описанию, команда git init была выполнена не в корневой директории проекта, а в одной из вложенных. В таком случае всё, что внутри этой вложенной директории, изнутри нее считается новым репозиторием (а снаружи — старым). При выполнении любой команды Git в некоторой директории происходит рекурсивный поиск репозитория снизу вверх. Т.е. проверяется текущая директория, потом ее родитель, потом родитель родителя и т.д. Как только находится директория .git, дальнейший поиск прекращается. Предположим, у нас есть такая структура. В корневой директории проекта A инициализирован репозиторий Git. A |-.git |-A/B |-A/C |-A/C/X |-A/C/Y |-A/C/Z |-A/D Теперь мы инициализируем новый репозиторий в директории A/C: $ cd C $ git init A |-.git |-A/B |-A/C |-.git |-A/C/X |-A/C/Y |-A/C/Z |-A/D Теперь наблюдаем следующую картину: При выполнении любой команды Git из директорий A, A/B, A/D, обнаруживается репозиторий в директории A. При выполнении любой команды Git из директории A/C и вложенных, обнаруживается репозиторий в директории С. Поскольку он только что создан, все файлы отображаются как новые. Чтобы исправить ситуацию, достаточно удалить .git из директории A/C: $ rm -rf A/C/.git

Ответ 3



если у вас не bare-репозиторий, то он состоит из собственно репозитория, хранящегося в каталоге .git, и т.н. working tree, т.е. файлов и каталогов, историю изменений которых вы и отслеживаете с помощью git. каталог (или файл) .git, если не используются, например, подмодули (submodules), должен быть только один. поищите в глубинах working tree другие каталоги .git. если обнаружите такой(-ие) каталог(-и), попробуйте переместить (не удаляя) его (их) в какое-нибудь другое место за пределами working tree и проверьте, всё ли в порядке с git log и git status.

Может ли Activity, обращающаяся к статическим полям быть причиной утечек памяти?

#android #memory_leaks


В приложении время от времени при логгировании возникают длиные цепочки, сообщающие
о работе GC, что, по моему мнению, говорит о некоторых утечках в памяти, так как если
бы все работало на ура, то не было бы огромного количества сообщений об аллокациях памяти. 

Стал вопрос оптимизации, ищу причины. Первая идея и мысль связана с логикой загрузки
приложения. Сначала я показываю активити-заглушку (пока загружаются все необходимые
данные), эти данные пишутся в статические поля некоторых классов для последующих операций,
то есть идет обращение к статическим ссылкам, которые не умирают. 

После вызывается finish, и я показываю активити, в которой идет все взаимодействие
юзера с приложением. Может обращение к статическим ссылкам быть одной из причин того,
что, например, активити не убивается при вызове finish и образуется утечка памяти?
    


Ответы

Ответ 1



Вообще, нет особой разницы в этом плане Java это чистая, или Android приложение. Хотя, я особо никогда не вникал в разницу между Dalvik и Hotspot jvm, но предполагаю, что GC работает примерно одинаково. Memory leak у вас может быть в случае, если static переменная в себе содержит линк на другой объект. Если вам уже не нужен этот объект, то, если вы забудете обнулить ссылку, он всё равно будет в памяти. Хотя, это сложно назвать именно утечкой, если брать в расчёт, что ссылка будет жить на протяжении жизни приложения. Формально, это будет перманентный синглтон. Может обращение к статическим ссылкам быть одной из причин того, что, например, активити не убивается при вызове finish и образуется утечка памяти? Вот это довольно интересный вопрос в контексте Android, так как в этой ОС жизненный цикл приложений весьма, хм, специфичный. Тут всё зависит, само собой, от того, что за переменные у вас статические. Так как в вопросе нету специфики, будет рассматривать абстрактные ситуации. Часто утечки бывают из-за context'а. Скажем, есть у вас: public class SomeClass extends SurfaceView { private static Context myContext; public MyInnerClass(Context context){ myContext = context; // здесь начинается печалька } } Даже если не останется ссылок на объекты SomeClass (вспомним, что если нет ссылок, то GC должен быть очистить память), скажем, когда Activity, которая показывала эту SurfaceView закрылась, статичная ссылка на Context (и любые другие статические переменные / константы в SomeClass останутся). Вы можете считать, что все они станут причиной утечкой памяти, так как нет возможности GC утилизировать Context. В контексте Android'а, могу сказать, что причиной утечек часто бывают как раз-такие ссылки на Activity, Context, View, Drawable, да и вообще любые объекты, которые содержат в себе ссылки на контейнер Activity или Context. Так же причиной могут быть нестатические внутренние классы (типо Runnable, которые могут содержать ссылки на Activity). Если же развить тему и уточнить вопрос: а вы уверены, что причина именно в static переменных? Handler с postDelay точно так же может стать причиной утечек. Можно почитать про возможные причины в блоге badoo. Или же в Android developers blog посмотреть пример утечек с Drawable. По сему, я бы сначала с помощью соответствующих утилит выявил, где конкретно утечки происходят, а уже от этого и отталкивался бы потом.

Ответ 2



Насколько я понимаю работу системы Android, то при вызове finish() активити вовсе принудительно не уничтожается (не удаляется из памяти), она только помечается "не активной" - происходит вызов методов завершения (onPause(), onStop(), onDestroy()) и манипуляции со списком переходов. На передний план отображается активити с вершины стека. Сама же "финишированная" активити существует в памяти до того момента, пока GC не решит провести очистку по каким то своим соображениям. Соответственно, если активити имеет статические ссылки (которые существуют до окончания работы приложения), то уничтожена сборщиком мусора она не будет, даже не смотря на то, что и помечена "не активной", так как есть жесткая ссылка - на лицо явная утечка памяти. Вообще, Android Studio в последней версии (1.5 на сегодняшний день) включает Memory Monitor для поиска утечек (инструменты HPROF Viewer и Allocation Tracker). Разумно будет воспользоваться ими и посмотреть, что же происходит. Здесь вы можете принудительно вызвать GC (очистку памяти) и проследить за судьбой своей активити и что ее держит в этом бренном мире. Очень часто там ждут неожиданные сюрпризы. Смотрите так же статью по утечкам памяти на офф.сайте

Ответ 3



Не нужно хранить глобальные переменные в активити. Если Вам нужно сохранить переменные на весь жизненный цикл приложения, то есть 2 варианта, которые позволяют это сделать: Первый - это создать свой класс наследник Application, где определить эти глобальные переменные и написать гетеры и сеттеры для них. public class MyApplication extends Application { private MyCustomObject myGlobalField; @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); } @Override public void onCreate() { super.onCreate(); } @Override public void onLowMemory() { super.onLowMemory(); } @Override public void onTerminate() { super.onTerminate(); } public MyCustomObject getMyGlobalField() { return this.myGlobalField; } public void setMyGlobalField(MyCustomObject myGlobalField) this.myGlobalField = myGlobalField; } } Не забудьте указать Ваш класс приложения в манифесте: Затем, в Ваших активити вы получаете доступ к этим полям: MyApplication myApplication = (MyApplication)getApplicationContext(); MyCustomObject myGlobalField = myApplication.getMyGlobalField(); Второй вариант - это хранить все глобальные данные в SharedPreferences. Но, это вариант не подходит, если вы хотите сохранить свои custom Object, а не обычные примитивы.

Ответ 4



Хотя статические переменные и нарушают принцип взаимодействия между Activity (правильно передавать данные через Intent, но придется научить все свои объекты сериализоваться), но утечек памяти это вызывать не должно. Надо искать в других местах. Например, стандартная утилита из Android SDK 'android-sdk\tools\monitor.bat' имеет минимальный функционал для отслеживания выделения памяти.

C#. В чем разница между реализацией обобщенного класса через параметризацию типом и через тип object?

#c_sharp #generics #объекты #c_sharp_faq


Меня интересует этот вопрос с точки зрения производительности. Про то что object
требует приведение типов и из-за этого можно неявно допустить ошибку, я знаю.

UPD. Этот вопрос был задан на собеседовании. В качестве примера добавил 2 класса:

class SomeClass
{
    private T[] storage;
}

class SomeClass
{
    private object[] storage;
}

    


Ответы

Ответ 1



В том случае, если вы используете значимые типы, вы получаете дополнительные накладные расходы на упаковку и последующую распаковку. var numbers = new List(); numbers.Add(1); // упаковка №1 numbers.Add(2); // упаковка №2 int n1 = (int)numbers[0]; // распаковка №1 int n2 = (int)numbers[1]; // распаковка №2 Ну и как вы сами упомянули, такое использование не является типобезопасным. Обобщенные коллекции и были введены в .NET 2.0 как раз для того, чтобы избежать этих проблем. В более ранних версиях использовался ArrayList, который, по большому счету, является аналогом List. UPD В приведенном вами примере все то же самое: в случае, если в массив storage будут добавляться экземпляры значимых типов, будет происходить упаковка.

Ответ 2



Преимущества обобщенного кода, над не обобщенным: Безопасность типов. Когда обобщенный алгоритм применяется с конкретным типом, компилятор и CLR понимают это и следят за тем, чтобы в алгоритме использовались лишь объекты, совместимые с этим типом данных. Попытка использования несовместимого объекта приведет к ошибке на этапе компиляции или исключению во время выполнения. Объявив SomeType some = new SomeType() в вашей переменной storage можно будет хранить только объекты типа Stream или производные от него. Проверка будет происходить на этапе написания кода, т.е. сразу же выдаст предупреждение, в отличие от object - на этапе выполнения. Более простой и понятный код. Поскольку компилятор обеспечивает безопасность типов, в исходном тексте требуется меньше операция приведения типов, а такой код проще писать и сопровождать. Повышение производительности. До появления обобщений один из способов определения обобщенного алгоритма заключался в таком определении всех его членов, чтобы они «умели» работать с типом данных Object. Чтобы алгоритм работал с экземплярами значимого типа, перед вызовом членов алгоритма среда CLR должна была упаковать этот экземпляр. Упаковка требует выделения памяти в управляемой куче, что приводит к более частым процедурам уборки мусора, а это, в свою очередь, снижает производительность приложения. Поскольку обобщенный алгоритм можно создать для работы с конкретным значимым типом, экземпляры значимого типа могут передаваться по значению и CLR не нужно выполнять упаковку. Операции приведения типа также не нужны, поэтому CLR не нужно контролировать безопасность типов при их преобразовании, что также ускоряет работу кода. Для оценки производительности был использован не обобщенный ArrayList из библиотеки классов FCL и обобщенный List. Метод Main - вызов двух тестов public static void Main() { ValueTypePerfTest(); ReferenceTypePerfTest(); } Тестирование значимых типов: private static void ValueTypePerfTest() { const Int32 count = 10000000; using (new OperationTimer("List")) { List l = new List(); for (Int32 n = 0; n < count; n++) {          l.Add(n);                 // Без упаковки          Int32 x = l[n];           // Без распаковки }        l = null; // Для удаления в процессе уборки мусора } using (new OperationTimer("ArrayList of Int32")) { ArrayList a = new ArrayList(); for (Int32 n = 0; n < count; n++) {          a.Add(n);                  // Упаковка          Int32 x = (Int32) a[n];    // Распаковка }        a = null; // Для удаления в процессе уборки мусора } } Тестирование ссылочных типов: private static void ReferenceTypePerfTest() { const Int32 count = 10000000; using (new OperationTimer("List")) { List l = new List(); for (Int32 n = 0; n < count; n++) {        l.Add("X");                   // Копирование ссылки        String x = l[n];              // Копирование ссылки }      l = null; // Для удаления в процессе уборки мусора } using (new OperationTimer("ArrayList of String")) { ArrayList a = new ArrayList(); for (Int32 n = 0; n < count; n++) {          a.Add("X");                 // Копирование ссылки          String x = (String) a[n];   // Проверка преобразования        }                             // и копирование ссылки        a = null; // Для удаления в процессе уборки мусора } } Класс для оценки времени выполнения операций internal sealed class OperationTimer : IDisposable { private Int64 m_startTime; private String m_text; private Int32 m_collectionCount; public OperationTimer(String text) { PrepareForOperation(); m_text = text; m_collectionCount = GC.CollectionCount(0);      // Эта команда должна быть последней в этом методе      // для максимально точной оценки быстродействия m_startTime = Stopwatch.StartNew(); } public void Dispose() {      Console.WriteLine("{0} (GCs={1,3}) {2}", (m_stopwatch.Elapsed),         GC.CollectionCount(0), m_collectionCount, m_text); } private static void PrepareForOperation() { GC.Collect(); GC.WaitForPendingFinalizers(); GC.Collect(); } } Результаты тестирования 00:00:01.6246959 (GCs= 6) List 00:00:10.8555008 (GCs=390) ArrayList of Int32 00:00:02.5427847 (GCs= 4) List 00:00:02.7944831 (GCs= 7) ArrayList of String Вывод С типом Int32 обобщенный алгоритм List работает гораздо быстрее, чем не обобщенный алгоритм ArrayList. Более того, разница огромная: 1,6 секунды против 11 секунд, то есть в 7 раз быстрее! Кроме того, использование значимого типа (Int32) с алгоритмом ArrayList требует множества операций упаковки, и, как результат, 390 процедур уборки мусора, а в алгоритме List их всего 6. Результаты тестирования для ссылочного типа не столь впечатляющие: временные показатели и число операций уборки мусора здесь примерно одинаковы. Поэтому в данном случае у обобщенного алгоритма List реальных преимуществ нет. Тем не менее помните, что применение обобщенного алгоритма значительно упрощает код и контроль типов при компиляции. Источник: Jeffrey Richter "CLR via C#"