Генерация объектно-ориентированного кода. Технология CodeDom
Результирующий код на C# и Visual Basic
На языке C# будет сгенерирован следующий код (без автоматически добавленных при генерации комментариев):
namespace MyNameSpace { using System; using System.Text; using System.Collections.Generic; public class MyClass { public static void Main() { System.Collections.Generic.List<string> MyList = new System.Collections.Generic.List<string>(); string temp = System.Console.ReadLine(); int MyCount = System.Convert.ToInt32(temp); for (int i = 0; i < MyCount; i = i + 1) { MyList.Add(("item " + i.ToString())); System.Console.WriteLine(MyList[i]); } System.Console.ReadKey(); } } }Пример 6.14.
Как видим, чтобы сгенерировать одну строчку кода, нужно написать от 5 и более строчек кода генератора. Однако такая, казалось бы неэффективная, затрата труда может компенсироваться тем, что генератор можно применить для создания гораздо большего объема кода, чем код самого генератора.
На Visual Basic код будет выглядеть так (без автоматически добавленных при генерации комментариев):
Option Strict Off Option Explicit On Imports System Imports System.Collections.Generic Imports System.Text Namespace MyNameSpace Public Class [MyClass] Public Shared Sub Main() Dim MyList As System.Collections.Generic.List(Of String) = New System.Collections.Generic.List(Of String) Dim temp As String = System.Console.ReadLine() Dim MyCount As Integer = System.Convert.ToInt32(temp) Dim i As Integer = 0 Do While i < MyCount MyList.Add(("item " + i.ToString())) System.Console.WriteLine(MyList(i)) i = i + 1 Loop System.Console.ReadKey() End Sub End Class End NamespaceПример 6.15.
Хоть сформированный код на Visual Basic не содержит синтаксических ошибок, не спешите внедрять его - он не будет работать корректно. Это объясняется тем, что правила создания форм на Visual Basic и C# отличаются. И даже когда сгенерированный код на одном языке работает прекрасно, если его создать на другом, то этот код может работать не так, как ожидалось, либо вообще не работать. Это открывает нам следующий недостаток CodeDom - если необходимо выполнять генерацию для нескольких языков, нужно очень тщательно учитывать различия между ними и стараться применять такие программные конструкции, которые являются общими для этих языков.
Компиляция кода
Также эту технологию можно использовать для динамической компиляции кода. Компилировать можно как из файла кода, так и из CompileUnit.
string path = @"C:\Users\agpx\Desktop\Code Generation\Code Generation Lections\examples CodeDom\MyGenericExample"; string outputexe = path + ".exe"; CodeDomProvider provider; if (lang == "C#") { provider = new CSharpCodeProvider(); path += ".cs"; } else { provider = new VBCodeProvider(); path += ".vb"; } //Установка параметров для выполнения компиляции CompilerParameters cp = new CompilerParameters(); //Добавление ссылки на сборку cp.ReferencedAssemblies.AddRange( new string[] {"System.dll", "System.Data.dll", "System.Drawing.dll", "System.Windows.Forms.dll", "System.XML.dll"}); //Уставка необходимости генерации исполняемого файла, а не библиотеки cp.GenerateExecutable = true; //Установка имени и пути файла, который будет генерироваться cp.OutputAssembly = outputexe; //Сохранить сборку как физический файл, а не в памяти cp.GenerateInMemory = false; //Запустить компиляцию CompilerResults cr = provider.CompileAssemblyFromDom(cp, compileUnit);Пример 6.16.
Преимущества
Каковы же преимущества применения CodeDom? Они следующие:
- Технология CodeDom предоставляет не зависящую от языка реализации единую модель для представления структуры программного кода.
- Поддержка генерации на нескольких языках. Так как для представления программного кода применяется одна модель, вывод сгенерированного кода можно осуществлять для множества языков, поддерживающих технологию CodeDom. Это означает также то, что генератор можно внедрять и для еще не созданных языков, которые появятся в будущем. Внедрение поддержки CodeDom в будущих языках будет достаточным для возможности генерации.
- Динамическая компиляция и запуск программного кода. В пространстве имен CodeDom имеются классы, позволяющие динамически компилировать программный код в исполняемые файлы или библиотеки. Также можно динамически запускать скомпилированные программы.
- По мере развития технологии CodeDom есть вероятность появления возможности конвертировать исходный код программ из одного языка в другой. Например, из C# в Visual Basic и наоборот.
Недостатки
Имеются следующие недостатки применения:
- Трудоемкость создания генератора. Для генерации одной строчки кода может потребоваться написать около десятка строк довольно непростого кода. Но этот недостаток можно уменьшить, группируя наборы стандартных операций по созданию кода в функции и процедуры.
- Не все характеристики кода поддерживаются, кроме тех, которые являются общими для всех языков. Например, нет вложенных пространств имен, нет поддержки списков переменных в объявлении переменных, нет поддержки модификатора unsafe в C#. Хотя эти ограничения можно обойти применением класса CodeSnippetExpression, но такой прием сильно уменьшает гибкость генерации для разных языков.
- Нет гарантии, что сгенерированный код будет компилироваться. После генерации нужно убедиться, что получившийся код корректен.
- Поведение сгенерированного кода на разных языках может быть разным, вплоть до того, что на одном языке код будет компилироваться, а на другом - нет. И даже если на всех языках код скомпилируется успешно, нюансы поведения сгенерированного кода на разных языках могут отличаться.
- Программы с применением CodeDom не обладают хорошей наглядностью шаблонов. Выполнение поиска нужного участка шаблонного кода является сложным занятием.
- Сгенерировать код можно будет только в том стиле и формате, который встроен по умолчанию в CodeDom. Если же нужно создать код, стиль которого отличается от стандартно генерируемого, то придется самостоятельно писать расширения для CodeDom.
Заключение
Если есть необходимость генерировать один и тот же код сразу на нескольких языках, то CodeDom может оказаться весьма полезным инструментом. Эта технология активно используется самой компанией Microsoft. Особенно часто она применяется в Visual Studio, где при выполнении каких-либо действий в зависимости от выбранного языка автоматически создаются объекты и программный код. Теоретически, есть возможность генерировать код для любого языка, для которого существует провайдер CodeDom. Провайдер также включает поддержку компиляции сгенерированного кода, при этом набор объектов CodeDom конвертируется напрямую в MSIL.
CodeDom поддерживает любой язык, для которого есть провайдер. Разработчики также могут добавить новый язык, создав для него свой провайдер.
Однако, если требуется генерировать код только для одном языке, то применение этой технологии может не оправдать себя. Гораздо проще может оказаться применение шаблонов либо манипуляция строками текста.
CodeDom абстрактен настолько, что позволяет выполнять генерацию для нескольких языков. Однако и сложен настолько, что если не нужно генерировать код одновременно на нескольких языках, то его применение может быть невыгодным, затраты времени могут оказаться слишком большими.
При применении CodesDom следует:
- стараться избегать прямых вставок блоков текста с помощью класса CodeSnippetExpression. Это снижает гибкость кода при генерации на нескольких языках и может увеличить число ошибок;
- тестировать код на всех языках, которые требуются. Даже если сгенерированный код прекрасно компилируется для одного языка, то код на другом языке может оказаться нерабочим.