Code Generation and Saving |
Examples of generating C# code objects and saving them as text.
// Create a new solution and project const string path = "Generated\\"; Solution solution = new Solution(path + "Generated"); // file extension will default to '.sln' if omitted Project project = solution.CreateProject(path + "Generated"); // file extension will default to '.csproj' if omitted project.OutputType = Project.OutputTypes.Exe; // Output type will default to Library if not set // In order to have external symbolic references resolved, assembly references must be added, then loaded. // However, this is not necessary in this case, since we are only saving the generated code, and UnresolvedRefs // will save just fine using their text names. //project.AddDefaultAssemblyReferences(); // Add commonly used assembly references //project.LoadReferencedAssembliesAndTypes(); // Add a file to the project, and put some code in it CodeUnit codeUnit = project.CreateCodeUnit(path + "Program"); // file extension will default to '.cs' if omitted codeUnit.Add( new UsingDirective(project.ParseName("System")), new UsingDirective(project.ParseName("System.Collections.Generic")), new UsingDirective(project.ParseName("System.Linq")), new UsingDirective(project.ParseName("System.Text")), new NamespaceDecl(project.ParseName("Generated")) { Body = { new ClassDecl("Program") { Body = { new MethodDecl("Main", typeof(void), Modifiers.Static, new ParameterDecl("args", typeof(string[]))) { Body = { new Comment("Add code here") } }} }} }); // External references will be unresolved, unless we add and load assembly references as shown up above Log.WriteLine("UnresolvedRefs = " + codeUnit.CalculateMetrics().UnresolvedReferences); // If code is simple enough, object initializers can be used as shown above, but when there are symbolic references, // objects need to be assigned to local variables so that references can be easily generated. This is shown in the // next example, which generates a code fragment. Also, note the use of 'project.ParseName()' in the UsingDirectives // (which aren't necessary in this case, just shown as examples) - this is a helper method that parses a string into // an expression of NamespaceRef and/or TypeRefs and Dot operators if present. If the namespaces or types can't be // found in scope (in the referenced assemblies), then UnresolvedRefs will be used. Manually created code is usually // created with resolved references, and so needs no resolving, but UnresolvedRefs can also be used if desired. // Manually created objects will be default formatted - there's no reason to worry about braces, parentheses, commas, // semi-colons, newlines, whitespace, or anything else that is purely syntax-related and not semantic. However, the // default formatting can be overridden using various properties, such as NewLines, HasBraces, HasParens. You may // use '.IsFirstOnLine = true' to force an object to start on a new line (or '.NewLines = 1'), or '.IsSingleLine = true' // to force an Expression or Statement to be formatted on a single line. // For more examples of manual code creation, see ManualTests.GenerateFullTest() in the Nova.Test solution, which // generates a replica of the related FullTest.cs file, which includes most C# language features. // Save the entire solution, including all projects and files solution.SaveAll(); // You may also save a single project and all of its files using 'project.SaveAll()', or you may save just an individual // solution, project, or file by calling '.Save()' on it. Also, '.SaveAs(string)' methods are provided to save under // a different file name.
// Any code object can be created without a parent, and with whatever children are desired. For example, you might // create a ClassDecl or a MethodDecl, or you can create a BlockDecl (a block of statements delimited by braces, as // shown below), or you can create an individual Statement or Expression of any kind. var sum = new LocalDecl("sum", typeof(int), 0); var flag = new LocalDecl("flag", typeof(bool), false) { EOLComment = "some flag" }; var i = new LocalDecl("i", typeof(int), 1); var @for = new For(i, new LessThan(i, 10), new Increment(i), new Block(new AddAssign(sum, i), new Assignment(flag, true))) { Comment = "calculate a sum" }; var block = new BlockDecl(sum, flag, @for); // When a named code object (such as 'i' above), is used where an Expression is expected, there are implicit conversion // operators which create SymbolicRefs (in this case, a LocalRef), avoiding the need to call '.CreateRef()' each time. // Likewise, Types (such as 'typeof(int)') used for Expressions are implicitly converted to TypeRefs (avoiding the need to // use 'new TypeRef(typeof(int))', and literals (such as '0' or 'false') are implicitly converted to Literals (avoiding // the need to use 'new Literal(0)' or 'new Literal(false)'. // Only Solution, Project, and CodeUnit objects implement the IFile interface and are mapped to files. For all other code // objects, there is no '.Save()' method. However, you may convert any code object to a string using 'AsString()' // (the 'ToString()' method generates only a 'description' instead of the full object, because otherwise it would cause // performance issues when working in the debugger if all objects rendered all of their child objects). Log.WriteLine(block.AsString());