Накалу страстей такой беседы позавидовали бы религиозные фанатики. Единственное, что спасает противников от крестовых походов друг на друга, вооружившись вилами и LangSpec’ами — то, что они разделены интернетом.
Так жить нельзя, господа. Мне захотелось исправить эту ситуацию, и выступить с одной из сторон. Этим постом я попробую нанести сообществу непоправимую пользу и разобраться с мифами, на обсуждение которых, а не на взаимное членовредительство, к сожалению, и уходят силы спорщиков. А так как я в своё время перелез с C++ на C# и всё вокруг него, то я буду развенчивать негативные мифы, прибавлять позитива и всячески приукрашивать действительность — а как без этого. И — заметьте — это обойдется совершенно бесплатно для M$. Ну а сделать я это хочу в формате Q&A.
#. C# и CLR — это такая VM, т.е. интерпретатор, а, значит, очень медленно и печально. Мне нужно, чтобы было быстро, очень быстро!
Я не буду рассказывать тут, чем компиляция отличается от интерпретации. Просто хочу заметить вот что: джентльмены, недавний опрос на Хабрахабре показал — большинство разработчиков так или иначе используют “управляемые” языки, которые компилируются не в нативный код, а в байт-коды, исполняемые интерпретаторами — непосредственными или компилирующего типа. Всякие TraceMonkey, LuaJIT, YARV как раз примеры для последней классификации. Это означает, что переход на другую платформу сходной архитектуры заведомо не сделает приложение медленнее. В этом смысле беспокоиться не о чем.
Однако, CLR — это sort of virtual machine, но это не интерпретатор. Еще раз повторюсь: MS.NET это НЕ ИНТЕПРЕТАТОР БАЙТКОДА. Специальный компилятор JIT постепенно преобразует байткод программы в нативный код, примерно такой же, который выдаёт компилятор C++. Текущая реализация CLR — MS.NET и Mono гарантируют, что ЛЮБОЙ код, который будет исполняться, преобразуется в нативный код. При этом для десктопов утверждение еще сильнее: любой код будет откомпилирован только один раз. Причём то, что он компилируется “на лету”, теоретически позволяет более оптимально использовать особенности конкретного процессора, а значит, сильнее соптимизировать код.
Более того, сравнение абсолютных цифр на бенчмарках показывает, что CLR оказывается на порядки эффективнее, чем популярные скриптовые языки типа JavaScript и Ruby, тоже использующие технологию JIT.
#. Языки со сборкой мусора отстают от языков типа C++ по скорости.
Верно, тут вы весьма близки к истине. Но, как и любой холиварщик, немного не договариваете. Правильно фраза будет звучать так: “корректно написанное и целиком вручную оптимизированное нативное приложение без ошибок, использующее специальные методики управления памятью, будет быстрее, чем приложение с автоматической сборкой мусора”.
Но для более-менее серьезного ПО создать такое приложение означает огромное количество затраченных усилий. Значительно превышающее то, которое потребуется для управляемого языка.
Именно поэтому появились языки высокого уровня — в длительной перспективе в среднем код, выдаваемый компилятором, будет содержать меньше ошибок и работать быстрее, чем написанный вручную.
И — да — тупые цифры не лгут: выделение памяти в языках со сборкой мусора выполняется БЫСТРЕЕ, и _не_ фрагментирует кучу, в отличие от с++. Обработка исключений в управляемых языках тоже выполняется быстрее.
А тут еще в дело вступает фактор времени, и стоимости разработки, включая количество ошибок. Потому что ошибка повреждения или утекшей памяти… хм… когда же я её видел в CLR последний раз? Лет 10 назад, не меньше.
#. Программы на CLR жрут очень много памяти. Прям вааще всё жрут, ничего не оставляют…
Хм. Сравнимое нагруженное Ruby-on-Rails-приложение на сервере кушает 100-150МБ RAM, примерно столько же, сколько и ASP.NET CLR сайт. Тут нет большой разницы.
Конечно, в небольших скриптовых задачах тот же Ruby оказывается гораздо эффективнее. Но вопрос не о скриптовых задачах — на проектах из реальной жизни, которые приносят деньги, аппетиты CLR выглядят соразмерными другим технологиям, и я не могу согласиться с определением “жрут очень много”.
#. Ну ладно, ладно, GC это хорошо. Но сборщик мусора — очень капризное животное, там есть огромное количество настроек. Их же никто не может корректно выставить — ручное вмешательство только вредит. GC в моём ZZZ работает и так! Сам!
Между прочим, CLR обладает одним из лучших сборщиков мусора на сегодняшний день. Первый его вариант был написан на LISPe, чтобы явственнее выразить семантику отношений между объектами в памяти и выполнить автоматический анализ корректности алгоритма, а затем переписан на С++. С тех пор прошло много времени, GC был обкатан миллионами разработчиков и не меньшим количеством проектов. Не течёт, что ни делай!
В качестве настроек выступает один ключ в файле конфигурации gcServer=”true/false”. Включает параллельную сборку мусора, а так же другие оптимизации. По умолчанию стоит в false, чтобы не мешать интерактивному режиму UI (работа gc незаметна для UI) на однопроцессорных машинах. В CLR 4.0 появились новые настройки, но суть та же — отлично работает “из коробки”, убирайте свои пассатижи подальше.
#. А в моём любимом языке ZZZ есть FFI, и поэтому я могу писать к нему расширения на С, если мне нужна скорость. Ни разу, правда, не писал, ну и что! Ведь могу же! А что в CLR/C#, надо всё переписывать на управляемый язык?
Очень рад за ZZZ. Вы удивитесь, но в CLR тоже присутствует возможность вызывать функции из нативных dll, написанных на старом добром С. И, конечно, передавать туда данные и получать обратно. Причем, в отличие от большинства FFI, вам не нужно проектировать dll под FFI — использовать соглашения по вызову и специальные типы данных. CLR всеяден, его можно гибко настроить на поедание почти любой библиотеки. Отдельным образом включена и автоматизирована поддержка COM, для более удобного доступа к возможностям Windows.
Это называется Interop/Platform Invoke
#. Окэ, я могу много чего написать на С. Но не буду же я писать сам всё! В .NET отсутствуют нужные бибилиотеки; а чтобы работать с БД, нужно покупать MSSQL за сто тыщ миллионов денег!
Вам и не нужно писать всё. В .NET присутствует отличная stdlib, называемая BCL (Base Class Library). Там есть многое из того, что вам понадобится: файлы, сокеты/сеть, http и web, регулярные выражения, SQL и работа с данными, xml и веб-сервисы и т.д.
Если вам нужно что-то, чего нет в BCL, скорее всего, такая библиотека уже есть. Либо можно воспользоваться нативной — так сделаны обертки к OpenGL и OpenAL, bass.dll (звук) и много чего.
Для .NET написаны провайдеры для MySQL и Oracle, SQLite и PostgresQL, они стабильны и отлично работают. Да что там SQL, есть и MongoDB и свои объектные БД, есть клиент к Memcache и RabbitMQ. Есть свои ServiceBus и MessageQueue, а API к существующим системам писать очень просто.
#. Писать для CLR можно только из Visual Studio. И только под Windows. И то, и то опять стоит денег.
Неправда. Есть SharpDevelop, который достаточно хорош для бесплатной среды; есть MonoDevelop, который тоже хорош, и работает как по Win, так и в *nix. Есть плагины к Eclipse; кстати, при помощи IKVM.NET для запуска Eclipse не нужна Java, хватает одного CLR.
Облегченная версия Visual Studio Express позволяет создавать полноценные приложения в Win. Бесплатного MS SQL Express хватит надолго для большинства проектов.
Есть инструменты для отладки, профилирования, налаживания процесса Continuous Integration, сами написанные на .NET. Есть свои инструменты типа make/ant — NAnt, msbuild.
Качай@Устанавливай!
#. Место CLR — на сервере. А Mono — страшная неюзабельная ненадежная гадость, не выросшая из пеленок MiguelDeIcaza’s Labs ©.
Точно. На сервере приложений и на веб-сервере — в CLR есть свои Rails (ASP.NET MVC), есть свой Hibernate и десятки других ORM. Подойдёт для всего. Ну это разве страшно — мы все постепенно переползаем в веб.
С другой стороны, создатели Unity3D с вами не согласны. Это такой плеер, который хостит CLR-среду прямо в вашем браузере, а программы-сценарии для него пишутся на .NET языках. Очень быстрый, красивый. 3D уже сейчас. Не надо ждать Flash Player с поддержкой GPU.
Кстати, а вы слышали, что приложения Mono компилируется и для iPhone и iPad (#MonoTouch)? Да и тот же Unity3D это умеет.
#. Использование CLR заставляет меня переходить на C#, я не хочу его учить!
И совершенно не нужно. Да, в C# наиболее полно доступны все возможности CLR, но никто не заставляет пользоваться именно им. CLR — это не только C#, это отличная платформа и BCL, предоставляющая качественную объектную модель и инструменты. Существует огромное количество языков — новых, таких, как Boo, Nemerle, F#, или ранее известных: Delphi, Ada, Lisp, VB, php, в качестве back-end использующих CLR.
С этой точки зрения CLR похожа на LLVM — предоставляет сервисы нижнего уровня, такие, как IL (байт-код) и JIT, сборка мусора, объектная модель, общая система типов, стандартная библиотека, система безопасности и т.д.
#. С# это быдлоязык для ыnterprise, он застрял в прошлом веке, а в моём языке ZZZ каждые полгода новые фишечки!
Да, C# сейчас плотно обосновался в low-cost секторе enterprise — всё благодаря его характеристикам: на нем достаточно просто писать, статическая типизация и управляемая среда позволяет исключить целый класс ошибок, присущих скриптовым языками или языкам более низкого уровня, IDE предоставляет доступ ко всем нужным инструментам в пару кликов, встроенная прямо в IDE документация и первоклассный IntelliSense.
Благодаря этому решения на C#/CLR обходятся не так дорого, как в Java.
В C# соблюдается принцип обратной совместимости, но это не мешает добавлять новые features в язык. Уже сейчас есть параметрический полиморфизм (это когда Vector), лямбда-функции и замыкания, впервые из всех языков появились LINQ (Language-integrated queries) на базе ограниченного цитирования, присутствует вывод типов, появился целый пласт DLR. В 5.0 версии появляется встроенная поддержка асинхронного программирования.
CLR/С# не так плох, как вы думаете, и это ничего не стоит проверить. Но если вам мало C#, есть F# (порт Caml) и Nemerle (гибрид C# и функционального языка), есть даже CLR C++ — берите из двух миров то, что вам надо, и совмещайте.
#. Так, алоэ, я кое-что вспомнил. О какой кросcплатформенности речь, когда чтобы запустить под Mono, мне надо всё перекомпилировать. Это же как в старом добром С, чем лучше-то?!
Очередная глупость. Я не знаю, кто вам сказал, но в целом полностью управляемое CLR-приложение, скомпилированное под Windows, НЕ НУЖНО ПЕРЕКОМПИЛИРОВАТЬ. Можно ПРОСТО перенести на Linux с установленным Mono, скомандовать mono myapp.exe, и оно запустится. И наоборот тоже работает. Я проверял.
Правда, здесь вступает в ход связывание с библиотеками. Это как gems в Ruby — если этот конкретный gem использует нативные библиотеки, то вам нужно установить эти самые нативные библиотеки. Но а вообще — полно чисто Ruby-гемов.
No magic, как-то так.
#. Приложения .NET используют реестр. Опять эта головная боль с управлением версиями ПО, установкой и удалением программ?
Нет. Все управляемые приложения .NET можно распространять по модели deploy-by-copy — скопируй в нужную папку и запускай оттуда. Они не лезут в реестр, не заглядывают в системные папки.
Если же вы хотите использовать общую управляемую библиотеку, то специальный механизм, называемый GAC (Global Assembly Cache), используя механизмы криптографии, позаботится об отсутствии дублирования, о том, что нужная вам библиотека — именно та и именно той версии, которую вы ждёте.
#. А вот мои знакомые программисты… Они говорили, что в комплект разработчика на Visual Studio IDE и C# входит обязательный ректальный виброзонд и инструкция по его непрерывному ношению!
OMFG O_o! Я абсолютно ответственно заявляю: ваших знакомых программистов обманули. Я бы порекомендовал вам пойти и помочь им отказаться от ношения самозваного зонда, но, боюсь, что они уже вошли во вкус и не смогут… Но ведь MS и CLR тут не при чём, так?
Выводы
Конечно, мне можно возразить. Буду рад, если кто-то захочет поправить меня или дополнить, а может быть, и опровергнуть.
В общем и целом, я рассказал, как обстоит дело, чем, надеюсь, причинил немало добра своим коллегам по цеху CLR. Надеюсь, теперь не будет дурацких вопросов, типа “зачем C#, если есть Python, а при нём GC”.
Идти в эту сторону по жизни или нет — выбор за вами. Ничего не мешает совмещать. Я пишу на .NET за еду и на Ruby для души.

