Генерация объектно-ориентированного кода. Технология CodeDom
Создание цикла
Рассмотрим код, создающий цикл:
//Создание цикла CodeVariableDeclarationStatement MyVariable = new CodeVariableDeclarationStatement(typeof(int), "i"); MyVariable.InitExpression = new CodeSnippetExpression("0"); CodeIterationStatement forLoop = new CodeIterationStatement(); forLoop.InitStatement = MyVariable; CodeAssignStatement assignment = new CodeAssignStatement( new CodeVariableReferenceExpression("i"), new CodeSnippetExpression("i + 1")); forLoop.IncrementStatement = assignment; forLoop.TestExpression = new CodeSnippetExpression("i < MyCount"); MyGenericMethod.Statements.Add(forLoop);Пример 6.8.
CodeIterationStatement используется для создания цикла for. CodeSnippetExpression подставляет блок текста в генерируемый код. Через свойство цикла InitStatement назначается условие инициации переменной цикла. А IncrementStatement применяется для задания условия увеличения счетчика переменной цикла. CodeAssignStatement назначает переменной новое значение. При этом CodeVariableReferenceExpression выводит имя переменной в генерируемый код. Через свойство цикла TestExpression назначается условие, при котором выполняется цикл.
Если мы сгенерируем код в таком виде, то содержимое цикла у нас будет пустым. Добавим несколько операторов в свойство Statements цикла:
CodeBinaryOperatorExpression operate = new CodeBinaryOperatorExpression( new CodePrimitiveExpression("item "), CodeBinaryOperatorType.Add, new CodeVariableReferenceExpression("i")); forLoop.Statements.Add(new CodeMethodInvokeExpression( new CodeMethodReferenceExpression( new CodeVariableReferenceExpression("MyList"), "Add"),operate));Пример 6.9.
Внутри цикла через CodeMethodReferenceExpression создается вызов метода Add. При этом в параметры передается выражение CodeBinaryOperatorExpression, представляющее собой бинарную операцию над двумя другими выражениями. Через перечисление CodeBinaryOperatorType указываем, что нам нужно сложение, то есть Add. В первой части выражения будет стоять строка "item ", а во второй части - переменная i.
Аналогично добавим вывод на консоль значения элемента списка:
CodeExpression expr = new CodeMethodInvokeExpression( new CodeMethodReferenceExpression( new CodeVariableReferenceExpression("System.Console"), "WriteLine"), new CodeArgumentReferenceExpression("MyList[i]")); forLoop.Statements.Add(expr);Пример 6.10.
CodeArgumentReferenceExpression служит для ссылки на выражение аргумента функции. В завершение в свойство Statements нашего метода добавим еще код для считывания клавиши:
CodeExpression readkey = new CodeMethodInvokeExpression( new CodeMethodReferenceExpression( new CodeVariableReferenceExpression("System.Console"), "ReadKey"), new CodeArgumentReferenceExpression("")); MyGenericMethod.Statements.Add(readkey);Пример 6.11.
Вывод результата генерации в файл
Мы рассмотрели возможности построения дерева CodeDom, однако нужно еще сгенерировать код и сохранить его. Для этого используется метод GenerateCodeFromCompileUnit провайдера:
private static void WriteGeneratedFile(CodeCompileUnit compileUnit, string lang) { string path = @"C:\Users\agpx\Desktop\Code Generation\Code Generation Lections\examples CodeDom\MyGenericExample"; CodeDomProvider provider; if (lang == "C#") { provider = new CSharpCodeProvider(); path += ".cs"; } else { provider = new VBCodeProvider(); path += ".vb"; } using (StreamWriter sw = new StreamWriter(path, false)) { IndentedTextWriter tw = new IndentedTextWriter(sw, " "); provider.GenerateCodeFromCompileUnit(compileUnit, tw, new CodeGeneratorOptions()); tw.Close(); } }Пример 6.12.
Соберем все вместе
А теперь посмотрим на созданную нами программу, объединим все предыдущие примеры и получим следующий результат:
//Создание дерева CodeDom CodeCompileUnit compileUnit = new CodeCompileUnit(); //Инициализация объекта пространства имен с заданием имени CodeNamespace MyGenericNameSpace = new CodeNamespace("MyNameSpace"); compileUnit.Namespaces.Add(MyGenericNameSpace); //Импортирование пространства имен MyGenericNameSpace.Imports.Add(new CodeNamespaceImport("System")); MyGenericNameSpace.Imports.Add(new CodeNamespaceImport("System.Text")); MyGenericNameSpace.Imports.Add(new CodeNamespaceImport("System.Collections.Generic")); //Объявление класса CodeTypeDeclaration MyGenericClass = new CodeTypeDeclaration("MyClass"); //MyGenericClass.Attributes = MemberAttributes.Static; //MyGenericClass.TypeAttributes = System.Reflection.TypeAttributes.NotPublic; //MyGenericClass.IsClass = true; MyGenericNameSpace.Types.Add(MyGenericClass); //Создание точки входа CodeEntryPointMethod MyGenericMethod = new CodeEntryPointMethod(); MyGenericClass.Members.Add(MyGenericMethod); //Объявление типа List<string> CodeTypeReference MyGenericListType = new CodeTypeReference(typeof(List<string>)); //Создание списка типа List<string> с именем MyList CodeVariableDeclarationStatement MyGenericListVariable = new CodeVariableDeclarationStatement(MyGenericListType,"MyList"); //Создание объекта позволяющего вызывать конструкторы CodeObjectCreateExpression MyObjectCreateExpression = new CodeObjectCreateExpression(); //Установка вызова конструктора MyObjectCreateExpression.CreateType = MyGenericListType; MyGenericListVariable.InitExpression = MyObjectCreateExpression; MyGenericMethod.Statements.Add(MyGenericListVariable); //Создание переменной temp с типом string CodeVariableDeclarationStatement MyTempVariable = new CodeVariableDeclarationStatement(typeof(System.String), "temp"); MyTempVariable.InitExpression = new CodeMethodInvokeExpression( new CodeTypeReferenceExpression("System.Console"), "ReadLine", new CodeArgumentReferenceExpression()); MyGenericMethod.Statements.Add(MyTempVariable); //Создание переменной MyCount с типом int CodeVariableDeclarationStatement MyCountVariable = new CodeVariableDeclarationStatement(typeof(System.Int32), "MyCount"); MyCountVariable.InitExpression = new CodeMethodInvokeExpression( new CodeTypeReferenceExpression("System.Convert"), "ToInt32", new CodeArgumentReferenceExpression("temp")); MyGenericMethod.Statements.Add(MyCountVariable); //Создание цикла CodeVariableDeclarationStatement MyVariable = new CodeVariableDeclarationStatement(typeof(int), "i"); CodeIterationStatement forLoop = new CodeIterationStatement(); MyVariable.InitExpression = new CodeSnippetExpression("0"); forLoop.InitStatement = MyVariable; CodeAssignStatement assignment = new CodeAssignStatement( new CodeVariableReferenceExpression("i"), new CodeSnippetExpression("i + 1")); forLoop.IncrementStatement = assignment; forLoop.TestExpression = new CodeSnippetExpression("i < MyCount"); MyGenericMethod.Statements.Add(forLoop); CodeBinaryOperatorExpression operate = new CodeBinaryOperatorExpression( new CodePrimitiveExpression("item "), CodeBinaryOperatorType.Add, new CodeVariableReferenceExpression("i.ToString()")); forLoop.Statements.Add(new CodeMethodInvokeExpression( new CodeMethodReferenceExpression( new CodeVariableReferenceExpression("MyList"), "Add"),operate)); CodeExpression expr = new CodeMethodInvokeExpression( new CodeMethodReferenceExpression( new CodeVariableReferenceExpression("System.Console"), "WriteLine"), new CodeArrayIndexerExpression( new CodeVariableReferenceExpression("MyList"), new CodeVariableReferenceExpression("i") )); forLoop.Statements.Add(expr); CodeExpression readkey = new CodeMethodInvokeExpression( new CodeMethodReferenceExpression( new CodeVariableReferenceExpression("System.Console"), "ReadKey"), new CodeArgumentReferenceExpression("")); MyGenericMethod.Statements.Add(readkey); WriteGeneratedFile(compileUnit, "C#"); WriteGeneratedFile(compileUnit, "VB");Пример 6.13.