.NET
19 August 2009

Динамическая компиляция кода в 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

+25
27.2k 67
Comments 35
Top of the day