This is a small .NET library that enables easy C# code generation based on Classes and its elements.
It has ability to create ClassModels (POCO object generator) and Methods and write it to .cs files.
Can specify their Members (Constructor, Field, Property, Method) including Attributes and Parameters.
Defining namespace and using Directives is supported as well.
Library can also generate Enums and Interfaces, and create NestedClasses inside parent class.
BaseElement has Config for: IndentSize, Comment, CommentHasSummaryTag(df:true), AccessModifier, BuiltInDataType, CustomDataType, Name
Property more Config: IsGetOnly, IsAutoImplemented, GetterBody, SetterBody
CsGenerator Settings are: DefaultTabSize: 4 | OutputDirectory: "Output" |
-- List of components --
AccessModifier: public, private, protected, internal, protected_internal
BuiltInDataType: void, bool, byte, int, long, decimal, float, double, char, string, object
CommonDataType: DateTime, Guid
KeyWord: this, abstract, partial, static, new, virtual, override, sealed, const, async, readOnly
IndentType: None, Single, Double, Triple, Quadruple
For more complex code with indented segments specific Indent value should be set (prepend in a loop) for all internal lines/elements.
Package targets .NET Standard 2.0 so can be used both with .NetFramework and .NetCore / .Net (new unified)
Available on latest version.
Package manager console command for installation: Install-Package CsCodeGenerator
If you find this project useful you can mark it by leaving a Github Star ⭐
If you want help development, you can become a CONTRIBUTOR or you can make a DONATION: _ or _
⚡
Want to Contact for Development & Consulting: www.codis.tech (Quality Assurance)
Please read CONTRIBUTING for details on code of conduct, and the process for submitting pull requests.
When opening issues do write detailed explanation of the problem or feature with reproducible example.
**Also take a look into others packages:
Open source (MIT or cFOSS) authored .Net libraries (@Infopedia.io personal blog post)
| № | .Net library | Description |
|---|---|---|
| 1 | EFCore.BulkExtensions | EF Core Bulk CRUD Ops (Flagship Lib) |
| 2 | EFCore.UtilExtensions | EF Core Custom Annotations and AuditInfo |
| 3 | EFCore.FluentApiToAnnotation | Converting FluentApi configuration to Annotations |
| 4 | FixedWidthParserWriter | Reading & Writing fixed-width/flat data files |
| 5* | CsCodeGenerator | C# code generation based on Classes and elements |
| 6 | CsCodeExample | Examples of C# code in form of a simple tutorial |
Class Inheritance Hierarchy
|-*BaseElement
ClassModel :-----|Property:-- Field :----------|Constructor: Method :---------|EnumModel:------|InterfaceModel:-|Component Composition
CsGenerator||---Files||---Enums||---Classes||---Fields||---Constructors(DefaultConstructorfirst)||---Attributes|||---Parameters||||---Parameters||---Properties||---Attributes|||---Parameters||||---Parameters||---Methods||---Attributes|||---Parameters||||---Parameters||---NestedClasses(recursively)|--- ...Following is first example of ComplexNumber class and then creating its ClassModel for writing to Complex.cs
There are 2 option to configure it:
-one is using Fluent methods (A)
-and other with pure classes and properties (B)
Class we want to generate:
usingSystem;usingSystem.Model;namespaceCsCodeGenerator.Tests{[Description("Some class info")]publicpartialclassComplexNumber:SomeBaseClass,NumbericInterface{protectedconstdoublePI=3.14;privatestringremark;publicComplexNumber(){}publicComplexNumber(doublereal,doubleimaginary=0){Real=real;Imaginary=imaginary;}publicstaticstringDefaultFormat{get;}="a + b * i";publicdoubleReal{get;set;}publicdoubleImaginary{get;set;}publicvirtualstringRemark{get{returnremark;}set{remark=value;}}publicdoubleModul(){returnMath.Sqrt(Real*Real+Imaginary*Imaginary);}publicComplexNumberAdd(ComplexNumberinput){ComplexNumberresult=newComplexNumber();result.Real=Real+input.Real;result.Imaginary=Imaginary+input.Imaginary;returnresult;}/// <summary>// example of 2 KeyWords(new and virtual), usually here would be just virtual/// <summary>publicnewvirtualstringToString(){return$"({Real:0.00}, {Imaginary:0.00})";}}}A) Code to do it with Fluent methods - shorter code and more readable syntax. [WithElement() such as WithProperty].
Fluent calls are composed with BaseElement, when configuring deeper level we need to instanciate with New and call in the scope.
To be able to get subElements returned to main scope (like EFCore FluentAPI setting works) then more extension methods would be needed - one example is commented in source code: WithPropertyReturn.
varusingDirectives=newList<string>{"System;","System.ComponentModel;"};stringfileNameSpace="CsCodeGenerator.Tests";stringcomplexNumberText="ComplexNumber";// ClassvarclassModel=newClassModel();varindent=classModel.Indent;varcomplexNumberClass=(ClassModel)classModel.WithAttribute(newAttributeModel("Description").WithParameter(@"""Some class info""")).WithKeyWord(KeyWord.Partial).WithName(complexNumberText).WithConstructor(newConstructor(complexNumberText){BracesInNewLine=false}).WithConstructor(newConstructor(complexNumberText).WithParameter(BuiltInDataType.Double,"real",null).WithParameter(BuiltInDataType.Double,"imaginary","0").WithBodyLine("Real = real;").WithBodyLine("Imaginary = imaginary;")).WithField(newField(BuiltInDataType.Double,"PI"){SingleKeyWord=KeyWord.Const,DefaultValue="3.14"}).WithField(newField(BuiltInDataType.String,"remark"){AccessModifier=AccessModifier.Private}).WithProperty(newProperty(BuiltInDataType.String,"DefaultFormat"){SingleKeyWord=KeyWord.Static,IsGetOnly=true,DefaultValue=@"""a + b * i"""}).WithProperty(BuiltInDataType.Double,"Real").WithProperty(BuiltInDataType.Double,"Imaginary").WithProperty(newProperty(BuiltInDataType.String,"Remark"){SingleKeyWord=KeyWord.Virtual,IsAutoImplemented=false}.WithGetter("remark").WithSetter("remark = value")).WithMethod(newMethod(BuiltInDataType.Double,"Modul").WithBodyLine("return Math.Sqrt(Real * Real + Imaginary * Imaginary);")).WithMethod(newMethod(complexNumberText,"Add").WithParameter(complexNumberText,"input",null).WithBodyLine("ComplexNumber result = new ComplexNumber").WithBodyLine("{").AddIndent().WithBodyLine("Real = Real + input.Real,").WithBodyLine("Imaginary = Imaginary + input.Imaginary,").RemoveIndent().WithBodyLine("};").WithBodyLine("return result;")).WithMethod(newMethod(BuiltInDataType.String,"ToString"){KeyWords=[KeyWord.New,KeyWord.Virtual],Comment="example of 2 KeyWords(new and virtual), usually here would be just virtual"}.WithBodyLine("return $\"({Real:0.00}, {Imaginary:0.00})\";"));varcomplexNumberFile=newFileModel(complexNumberText);complexNumberFile.LoadUsingDirectives(usingDirectives);complexNumberFile.Namespace=fileNameSpace;complexNumberFile.Classes.Add(complexNumberClass);varcsGenerator=newCsGenerator();csGenerator.Files.Add(complexNumberFile);csGenerator.CreateFiles();B) Alternative way without Fluent:
varusingDirectives=newList<string>{"using System;","using System.ComponentModel;"};stringfileNameSpace=$"{Util.Namespace} CsCodeGenerator.Tests";stringcomplexNumberText="ComplexNumber";ClassModelcomplexNumberClass=newClassModel(complexNumberText);complexNumberClass.SingleKeyWord=KeyWord.Partial;// one way to set single KeyWord//complexNumberClass.KeyWords.Add(KeyWord.Partial); // or alternative waycomplexNumberClass.BaseClass="SomeBaseClass";complexNumberClass.Interfaces.Add("NumbericInterface)";vardescriptionAttribute=newAttributeModel("Description"){SingleParameter=newParameter(@"""Some class info""")};complexNumberClass.AddAttribute(descriptionAttribute);complexNumberClass.DefaultConstructor.IsVisible=true;ConstructorsecondConstructor=newConstructor(complexNumberClass.Name);secondConstructor.Parameters.Add(newParameter(BuiltInDataType.Double,"real"));secondConstructor.Parameters.Add(newParameter(BuiltInDataType.Double,"imaginary"){Value="0"});secondConstructor.BodyLines.Add("Real = real;");secondConstructor.BodyLines.Add("Imaginary = imaginary;");complexNumberClass.Constructors.Add(secondConstructor);varfields=newField[]{newField(BuiltInDataType.Double,"PI"){SingleKeyWord=KeyWord.Const,DefaultValue="3.14"},newField(BuiltInDataType.String,"remark"){AccessModifier=AccessModifier.Private},}.ToDictionary(a =>a.Name, a =>a);varproperties=newProperty[]{newProperty(BuiltInDataType.String,"DefaultFormat"){SingleKeyWord=KeyWord.Static,IsGetOnly=true,DefaultValue=@"""a + b * i"""},newProperty(BuiltInDataType.Double,"Real"),newProperty(BuiltInDataType.Double,"Imaginary"),newProperty(BuiltInDataType.String,"Remark"){SingleKeyWord=KeyWord.Virtual,IsAutoImplemented=false,GetterBody="remark",SetterBody="remark = value"},}.ToDictionary(a =>a.Name, a =>a);varmethods=newMethod[]{newMethod(BuiltInDataType.Double,"Modul"){BodyLines=newList<string>{"return Math.Sqrt(Real * Real + Imaginary * Imaginary);"}},newMethod(complexNumberText,"Add"){Parameters=newList<Parameter>{newParameter("ComplexNumber","input",null)},BodyLines=newList<string>{"ComplexNumber result = new ComplexNumber();","result.Real = Real + input.Real;","result.Imaginary = Imaginary + input.Imaginary;","return result;"}},newMethod(BuiltInDataType.String,"ToString"){Comment="example of 2 KeyWords(new and virtual), usually here just virtual",KeyWords=newList<KeyWord>{KeyWord.New,KeyWord.Virtual},BodyLines=newList<string>{"return $\"({Real:0.00}, {Imaginary:0.00})\";"}}}.ToDictionary(a =>a.Name, a =>a);complexNumberClass.Fields=fields;complexNumberClass.Properties=properties;complexNumberClass.Methods=methods;FileModelcomplexNumberFile=newFileModel(complexNumberText);complexNumberFile.LoadUsingDirectives(usingDirectives);complexNumberFile.Namespace=fileNameSpace;complexNumberFile.Classes.Add(complexNumberClass.Name,complexNumberClass);CsGeneratorcsGenerator=newCsGenerator();csGenerator.Files.Add(complexNumberFile.Name,complexNumberFile);csGenerator.CreateFiles();//Console.Write(complexNumberFile);