Динамическая компиляция кода в C#

    Использовать компилятор из кода C# достаточно просто. А вот зачем – это другой вопрос :).

    Hello World


    Напишем первый простой пример. Создаем консольное приложение и напишем следующий код:
    using System;<br>using System.CodeDom.Compiler;<br>using System.Collections.Generic;<br>using Microsoft.CSharp;<br><br>namespace ConsoleCompiler<br>{<br>  internal class Program<br>  {<br>    private static void Main(string[] args)<br>    {<br>      // Source code для компиляции<br>      string source =<br>      @"<br>namespace Foo<br>{<br>  public class Bar<br>  {<br>    static void Main(string[] args)<br>    {<br>      Bar.SayHello();<br>    }<br><br>    public static void SayHello()<br>    {<br>      System.Console.WriteLine(""Hello World"");<br>    }<br>  }<br>}<br>      ";<br><br>      // Настройки компиляции<br>      Dictionary<string, string> providerOptions = new Dictionary<string, string><br>        {<br>          {"CompilerVersion", "v3.5"}<br>        };<br>      CSharpCodeProvider provider = new CSharpCodeProvider(providerOptions);<br><br>      CompilerParameters compilerParams = new CompilerParameters<br>        {OutputAssembly = "D:\\Foo.EXE", GenerateExecutable = true};<br><br>      // Компиляция<br>      CompilerResults results = provider.CompileAssemblyFromSource(compilerParams, source);<br><br>      // Выводим информацию об ошибках<br>      Console.WriteLine("Number of Errors: {0}", results.Errors.Count);<br>      foreach (CompilerError err in results.Errors)<br>      {<br>        Console.WriteLine("ERROR {0}", err.ErrorText);<br>      }<br>    }<br>  }<br>}<br><br>* This source code was highlighted with Source Code Highlighter.

    Запускаем и проверяем:
    First Sample
    Первое, на что стоит обратить внимание – это использование двух пространств имен (namespace):
    • Microsoft.CSharp
    • System.CodeDom.Compiler
    В данных классах и содержится ключ к возможности компиляции. В нашем примере мы указываем что компилировать будем под .NET Framework 3.5, а так же указываем что мы хотим получить на выходе – Foo.exe, с возможностью запуска данного приложения.

    Пример посложнее, используем Linq


    Теперь давайте усложним наш пример, в компилируемый код добавим использование Linq:
    string source = @"<br>using System.Collections.Generic;<br>using System.Linq;<br><br>namespace Foo<br>{<br>  public class Bar<br>  {<br>    static void Main(string[] args)<br>    {<br>      Bar.SayHello();<br>    }<br><br>    public static void SayHello()<br>    {<br>      System.Console.WriteLine(""Hello World"");<br>      System.Console.WriteLine( string.Join("","", Enumerable.Range(0,10).Select(n=>n.ToString()).ToArray() ) );<br>    }<br>  }<br>}";<br><br>* This source code was highlighted with Source Code Highlighter.

    Добавленные строки помечены красным, если мы попробуем запустить предыдущий пример с измененным компилируемым кодом, то теперь мы увидим ошибки компиляции:
    Compiler Error
    Чтобы компиляция удалась, необходимо добавить в параметры компиляции ссылку на сборку System.Core.dll
    compilerParams.ReferencedAssemblies.Add("System.Core.Dll");<br><br>* This source code was highlighted with Source Code Highlighter.

    И теперь все будет работать:
    Linq Sample

    Используем созданную сборку в коде


    Теперь попробуем скомпилировать сборку Foo.dll вместо исполняемого файла, а так же сразу же после компиляции загрузить и использовать скомпилированный метод. Компилируемый код мы изменим, сделаем его попроще:
    stringsource = @"<br>using System.Collections.Generic;<br>using System.Linq;<br><br>namespace Foo<br>{<br>  public class Bar<br>  {<br>    public static void SayHello()<br>    {<br>      System.Console.WriteLine(""Hello World"");<br>      System.Console.WriteLine( string.Join("","", Enumerable.Range(0,10).Select(n=>n.ToString()).ToArray() ) );<br>    }<br>  }<br>}";<br><br>* This source code was highlighted with Source Code Highlighter.

    Изменим настройки компилятора, теперь будем собирать dll файл:
    const string outputAssembly = "D:\\Foo.dll";<br>CompilerParameters compilerParams = new CompilerParameters {OutputAssembly = outputAssembly, GenerateExecutable = false};<br><br>* This source code was highlighted with Source Code Highlighter.

    После компиляции и проверки ошибок используя Reflection (не забываем подключить пространство имен — using System.Reflection) вызовем метод Foo.Bar.SayHello() скомпилированной dll:
    Console.WriteLine("Try Assembly:");<br>Assembly assembly = Assembly.LoadFile(outputAssembly);<br>Type type = assembly.GetType("Foo.Bar");<br>MethodInfo method = type.GetMethod("SayHello");<br>method.Invoke(null, null);<br><br>* This source code was highlighted with Source Code Highlighter.

    Результат:
    Final Result
    Финальный пример можно скачать с mydrive.live.com.
    Информацию о динамической компиляции и основные примеры я взял отсюда: Saveen Reddy's blog — A Walkthrough of Dynamically Compiling C# code (Английский).
    Progg it
    Поделиться публикацией
    Ой, у вас баннер убежал!

    Ну. И что?
    Реклама
    Комментарии 35
    • +1
      Использую данныый метод для Eval javascript'а на стороне сервера, только сборку создаю в памяти
      • +4
        Ну да, одна из штатных возможностей языка, так работает, например, XmlSerializer. Вкупе с будущим dynamic — простор открывается совершенно невообразимый.

        Только пост почему-то не о чем.
        • +9
          Маленькое уточнение: отнимите Full Trust, и посмотрите, что получится — компилятор сейчас не является управляемым компонентном (он COM-объект), поэтому в отсутствие спец.привилегий скомпилировать удастся чуть менее, чем никак. В отличие от Boo или Nemerle, например. Так что если у вас чужой хостинг, будьте внимательны с permissions при развертывании такого решения — некоторые хостеры могут просто не дать Full Trust.

          А еще компилировать можно в память — если эта сборка больше никому не понадобится. А еще эмбеддить файлы внутрь сборки. А еще… Короче, ни о чем пост.
          • +2
            Эхх, кросспостеры :( Там у вас перевод качественнее, с картинками; а сюда в текст попали alt-ы к картинкам (Final Result, Linq Sample). Вы что, вслепую копипастили?
            • +1
              Не в слепую. Картинки должны быть.
              • 0
                Сорри, тогда заминусуйте :/
          • +1
            А еще любой топик или текст можно чем то дополнить. И что дальше?

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

            Так что какие то не понятные «А еще...» у вас получается. Если хотите дополнить данный топик, так я только буду рад и с удовольствием прочту.

            Удачи!
            • +1
              Пристыдили:)

              Я написал важное дополнение про Full Trust.
              Еще одно важное дополнение — начиная с .NET 3.5 SP 1 доступен .NET Client Profile — подмножество .NET FW; он относится к полноразмерному .net так же, как JRE относится к JDK. В настоящий момент туда включен компилятор csc, но пользоваться им нельзя (крайне не рекомендуется) — он может быть удален в будущей версии Client Profile.
              Пруф
            • 0
              расскажите как компилировать в память…
              дело в том, что он все равно пишет на диск временную сборку, а потом уже грузит в память
              • 0
                Ну так и компилировать :) Пишется-то она в .net temporary files, а то, что это — файлы на диске, а не в памяти — только конкретная реализация.
            • +1
              Динамическая компиляция удобна когда есть часто изменяемый код. То есть таким образом мы используем код на C# как скрипт. Таким образом удобно вносить небольшие изменения на стороне заказчика без перекомпиляции всего проекта.
              • +2
                CompilerResultsresults = provider.CompileAssemblyFromSource(compilerParams, source);
                Может всетаки stringsource?
                • +1
                  это у меня Source Code Highlighter или Writer пробелы пообрезал :) Переменная то нормально была объявлена string source, и собственно ее имя — source :)
                  Спасибо, все поправил вроде.
                • 0
                  Интересен как раз «другой вопрос» — то самое «зачем». Первое, что приходит в голову — генетические алгоритмы и полиморфные вирусы. Понятно, что можно и без динамической компиляции. Но с ней как-то естественнее :)

                  А кто что ещё интересненькое в эту тему может предложить?

                  Типа… автоматическую оптимизацию кода в зависимости от среды и вообще по обстоятельствам :) Код сам для себя время от времени запускает профайлер, анализирует, где у него образовались ботлнеки, пытается заменить эти куски кода на альтернативные (опционально — скачивая библиотеки, подходящие по интерфейсам).
                  • +2
                    У нас используются клиентские скрипты, которые содержат бизнес-логику. Служба поддержки на стороне заказчика имеет инструмент для того, чтобы «подкрутить» функционал (добавить кастомную валидацию, скрыть поля ввода) и т.п. Так как у нас толстый клиент на WinForms, такое решение вполне устраивает всех.

                    Тот сценарий, что вы рассказали, называется PGO (profile guided optimization), и пока что вне среды разработки он доступен в зачаточном состоянии (ниже уровнем, на этапе jit-компиляции IL — «горячие» и «холодные» участки).
                    • +1
                      Я, конечно же, подозревал (и надеялся), что что-то подобное реализовано или реализуется. Идейка ведь на поверхности. Другое дело, что реализация далеко не проста или не слишком-то эффективна пока что, раз так мало случаев применения.

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

                      А вообще было бы интересно дожить до того времени, когда единожды установленные на моём компе программы будут менять код со временем. Не просто потому, что авторы обновили версию, а по более интеллектуальным и избирательным принципам. Приспосабливаясь к среде, приспосабливаясь ко мне — без участия админа. Хотя бы и в сторону сокращения функционала — ведь жалкие проценты использую.
                    • +1
                      а) к примеру — вы пишите некое приложение (автоматизирующая бизнес область или нет), которое позволяет создавать методы для Domain объектов или функции, которые с ними работают. И в итоге вы предоставляете некий функционал, при помощи которого пользователь (в данному случае администратор системы) может создавать данные функции (не обязательно на C#) — главное что вы их запросто можете преобразовать в C# код, скомпилировать и положить предположим в dll рядом и потом вызывать.
                      б) создаете проект, чем то похожий на sql-ex, только на c#, чем не вариант?
                      • +1
                        а) Сборка на заказ (типа плагинов, в данном случае). Что-то где-то вспоминается, что некоторые так целые дистрибутивы собирают. Поставил галочки через web-интерфейс — сгенерился архив лично для тебя, качай. Даже админ не нужен.

                        б) С базами данных редко дело имел, а гуглиться разное всякое. Так что не знаю, «чем не вариант» :)
                        • +1
                          sql-ex.ru/ — это проект для обучения sql, вы пишите код, на сервере проверяется.
                      • +3
                        Динамическая компиляция используется для написания плагинов в проекте Oxite. Правда пока у них нет релиза с такой функциональностью, но можно посмотреть в рабочей версии.
                        • +2
                          Я компилирую поисковые запросы в предикаты и фильтрую коллекции объектов, так организован поиск в моих программах.

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

                          Только я использую Linq Expression Trees, а не предложенный метод. Linq Expression Trees в свою очередь используют класс DynamicMethod, наверное.
                          • +3
                            Также мне кажется, предложенный метод с предикатами можно использовать для Linq to SQL и Entity Framework — библиотеки смогут успешно преобразовать типизированный предикат в SQL-выражение, что позволит пользователю делать эффективные запросы к БД (выполняемые на стороне сервера БД) в стиле Google.

                            Я пока работал с коллекциями обычных объектов и не экспериментировал с поиском в БД.

                            Вообще, я имел в виду, что запрос, введённый пользователем, типа «time:<5m AND rating:>3» преобразовывается в скомпилированный типизированный делегат: (Track track) => track.Time < TimeSpan.FromMinutes(5) && track.Rating > 3;

                            Если Track — это одна из сущностей из БД, то предикат можно обратно преобразовать в SQL и сделать выборку на стороне сервера, а не фильтровать всю коллекцию объектов на стороне клиента, как если бы запрос не компилировался.
                        • НЛО прилетело и опубликовало эту надпись здесь
                          • +2
                            То что вам это не нужно, это ещё не значит, что это не нужно другим. Я вот представляю, как это можно использовать.

                            Спасибо автору топика!
                            • +5
                              у вас питон мозга, уважаемый
                              • +1
                                Вы мне, наверное, не поверите, но существуют программы, которые несколько более сложны, чем ваш хелловорлд. И если технология есть — ей найдется применение. Хуже, когда задача есть, а решить ее красиво нечем.

                                P.S. Ну так, для справки — на IronPython, который для .net, ваш хелловорлд будет выглядеть идентично. Так что вы не путайте теплое с мягким.
                                • 0
                                  А вам приходилось писать что-то кроме Хелловорлда, мне кажется нет, судя по тому как вы рассуждаете? У многих технологий уже есть занятая ими ниша, и это происходит обычно не потому что она (технология) хорошо пропиарена, а потому что она лучше других справлятся с возложенной на нее задачей или делает это на требуемом/достаточном уровне. Питон — хорошая технология, но есть задачи где его применения как минимум неуместно, что к примеру является следствием его скорости работы, которая не сравнится со скоростью работы .NET приложений.
                                  • НЛО прилетело и опубликовало эту надпись здесь
                                    • +1
                                      Ну не будут на питоне полностью игры писать, куски логики — да, но не полноценные игры. Полноценные графические приложения тоже не будут писать ибо есть проблемы с многопоточностью и опять таки с прозводительностью. Приложения на .NET по скорости работы весьма адекватны, под Windows единственная альтернатива это С++, но страдает скорость разработки.
                                      • НЛО прилетело и опубликовало эту надпись здесь
                                  • +1
                                    Стоит прочитать топик более внимательно.
                                    • +2
                                      Вы чем недовольны? Вот я вам динамически компилирую и запускаю Hello-World на C#. Что не устраивает?

                                      DynamicMethod meth = new DynamicMethod("", null, null);
                                      ILGenerator il = meth.GetILGenerator();
                                      il.EmitWriteLine("Hello, World!");
                                      il.Emit(OpCodes.Ret);
                                      
                                      Action hello = (Action) meth.CreateDelegate(typeof(Action));
                                      hello();
                                      


                                      Кстати, я сначала думал, что в топике именно об этом способе пойдет речь! Автор, надо было упомянуть и другие способы динамической компиляции!
                                      • 0
                                        Я тоже хотел побольше про DynamicMethod, хотя в продакшн никогда подобного не использовал ибо недебажится. То же самое с рефлекшном, в принципе.
                                    • 0
                                      Автору спасибо за труд. Наткнулся на статью через гуглопоиск. Изложенное пригодится мне для реализации моего проекта

                                      Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                      Самое читаемое