Testura.Code is a wrapper around the Roslyn API and used for generation, saving and compiling C# code. It provides methods and helpers to generate classes, methods, statements and expressions.
It provide helpers to generate:
- Classes
- Methods
- Parameters
- Arguments
- Attributes
- Fields
- Properties
But also simple statements like:
- Declaration statements (for example declare and assign variables)
- Iterations statements (for example for-loop)
- Jump statements (for example return)
- Selection statement (for example if-statements)
- Expression statements (for example invoke methods)
https://www.nuget.org/packages/Testura.Code
PM> Install-Package Testura.Code
Testura.Code have three different types of helpers:
Generators- The most basic kinds of code generators, for example fields, properties and modifiers.Statement- Helpers for regular statements and expressions, for example declare and assign a variable or invoke a method.Builders- Currently we have two builder - One class builder and one method builder. These have the highest abstraction and are easy to use.
Here is an example on how to generate, save and compile a simple hello world.
var@class=newClassBuilder("Program","HelloWorld").WithUsings("System").WithModifiers(Modifiers.Public).WithMethods(newMethodBuilder("Main").WithModifiers(Modifiers.Public,Modifiers.Static).WithParameters(newParameter("args",typeof(string[]))).WithBody(BodyGenerator.Create(Statement.Expression.Invoke("Console","WriteLine",newList<IArgument>(){newValueArgument("Hello world")}).AsStatement(),Statement.Expression.Invoke("Console","ReadLine").AsStatement())).Build()).Build();This code will generate following code:
usingSystem;namespaceHelloWorld{publicclassProgram{publicstaticvoidMain(String[]args){Console.WriteLine("Hello world");Console.ReadLine();}}}varsaver=newCodeSaver();// As a stringvargeneratedCode=saver.SaveCodeAsString(@class);// Or to filesaver.SaveCodeToFile(@class,@"/path/HelloWorld.cs");varcompiler=newCompiler();//To a dll// From stringvarresult=awaitcompiler.CompileSourceAsync(@"/path/HelloWorld.dll",generatedCode);// From filevarresult=awaitcompiler.CompileFilesAsync(@"/path/HelloWorld.dll",@"/path/HelloWorld.cs");//In memory (without creating a dll)// From stringvarresult=awaitcompiler.CompileSourceInMemoryAsync(generatedCode);// From filevarresult=awaitcompiler.CompileFilesInMemoryAsync(@"/path/HelloWorld.cs");varclassBuilder=newClassBuilder("Cat","Models");var@class=classBuilder.WithUsings("System").WithConstructor(ConstructorGenerator.Create("Cat",BodyGenerator.Create(Statement.Declaration.Assign("Name",ReferenceGenerator.Create(newVariableReference("name"))),Statement.Declaration.Assign("Age",ReferenceGenerator.Create(newVariableReference("age")))),newList<Parameter>{newParameter("name",typeof(string)),newParameter("age",typeof(int))},newList<Modifiers>{Modifiers.Public})).WithProperties(PropertyGenerator.Create(newAutoProperty("Name",typeof(string),PropertyTypes.GetAndSet,newList<Modifiers>{Modifiers.Public})),PropertyGenerator.Create(newAutoProperty("Age",typeof(int),PropertyTypes.GetAndSet,newList<Modifiers>{Modifiers.Public}))).Build();This code will generate following code:
usingSystem;namespaceModels{publicclassCat{publicCat(stringname,intage){Name=name;Age=age;}publicstringName{get;set;}publicintAge{get;set;}}}var@class=newClassBuilder("Cat","Models").WithUsings("System").With(newEnumBuildMember("MyEnum",newList<EnumMember>{new("EnumValueOne",2,newAttribute[]{newAttribute("MyAttribute"),}),newEnumMember("EnumValueTwo")},newList<Modifiers>{Modifiers.Public})).Build();This code will generate following code:
usingSystem;namespaceModels{publicclassCat{publicenumMyEnum{[MyAttribute]EnumValueOne=2,EnumValueTwo}}}var@class=newClassBuilder("Cat","Models",NamespaceType.FileScoped).WithUsings("System").WithFields(newField("_name",typeof(string),newList<Modifiers>(){Modifiers.Private}),newField("_age",typeof(int),newList<Modifiers>(){Modifiers.Private})).WithConstructor(ConstructorGenerator.Create("Cat",BodyGenerator.Create(Statement.Declaration.Assign("Name",ReferenceGenerator.Create(newVariableReference("name"))),Statement.Declaration.Assign("Age",ReferenceGenerator.Create(newVariableReference("age")))),newList<Parameter>{newParameter("name",typeof(string)),newParameter("age",typeof(int))},newList<Modifiers>{Modifiers.Public})).WithProperties(PropertyGenerator.Create(newBodyProperty("Name",typeof(string),BodyGenerator.Create(Statement.Jump.Return(newVariableReference("_name"))),BodyGenerator.Create(Statement.Declaration.Assign("_name",newValueKeywordReference())),newList<Modifiers>{Modifiers.Public})),PropertyGenerator.Create(newBodyProperty("Age",typeof(int),BodyGenerator.Create(Statement.Jump.Return(newVariableReference("_age"))),BodyGenerator.Create(Statement.Declaration.Assign("_age",newValueKeywordReference())),newList<Modifiers>{Modifiers.Public}))).Build();This code will generate following code:
usingSystem;namespaceModels;publicclassCat{privatestring_name;privateint_age;publicCat(stringname,intage){Name=name;Age=age;}publicstringName{get{return_name;}set{_name=value;}}publicintAge{get{return_age;}set{_age=value;}}}varrecord=newRecordBuilder("Cat","Models",NamespaceType.FileScoped).WithUsings("System").WithPrimaryConstructor(newParameter("Age",typeof(int))).Build();This code will generate following code:
usingSystem;namespaceModels;publicrecordCat(intAge);var@class=newClassBuilder("Cat","Models").WithUsings("System").WithMethods(newMethodBuilder("MyMethod").WithModifiers(Modifiers.Public,Modifiers.Static).WithOperatorOverloading(Operators.Increment).WithParameters(newParameter("MyParameter",typeof(string))).WithBody(BodyGenerator.Create(Statement.Declaration.Declare("hello",typeof(int)).WithComment("My comment above").WithComment("hej"),Statement.Declaration.Declare("hello",typeof(int)).WithComment("My comment to the side",CommentPosition.Right))).Build()).Build();This code will generate following code:
usingSystem;namespaceModels{publicclassCat{publicstaticMyMethodoperator++(stringMyParameter){//hejinthello;inthello;//My comment to the side}}}var@class=newClassBuilder("NullTest","MyTest").WithUsings("System","NUnit.Framework").WithModifiers(Modifiers.Public).WithMethods(newMethodBuilder("SetUp").WithAttributes(newAttribute("SetUp")).WithModifiers(Modifiers.Public).Build(),newMethodBuilder("Test_WhenAddingNumber_ShouldBeCorrectSum").WithAttributes(newAttribute("Test")).WithModifiers(Modifiers.Public).WithBody(BodyGenerator.Create(Statement.Declaration.Declare("myList",typeof(List<int>)),NunitAssertGenerator.Throws(newVariableReference("myList",newMethodReference("First")),typeof(ArgumentNullException)))).Build()).Build();This code will generate following code:
usingSystem;usingNUnit.Framework;namespaceMyTest{publicclassNullTest{[SetUp]publicvoidSetUp(){}[Test]publicvoidTest_WhenAddingNumber_ShouldBeCorrectSum(){List<int>myList;Assert.Throws<ArgumentNullException>(()=>myList.First(),"");}}}If we miss a feature, syntax or statements - just create an issue or contact us and I'm sure we can add it.
It is also possible for you to contribute with your own feature. Simply add a pull request and we will look at it.
This project is licensed under the MIT License. See the LICENSE.md file for details.
Visit www.testura.net, twitter at @testuranet or email at mille.bostrom@testura.net
