CSharpFile
The main entry point for building C# files.Constructor
public CSharpFile(string @namespace, string relativeLocation)
public CSharpFile(string @namespace, string relativeLocation, ICSharpFileBuilderTemplate template)
The namespace for the file
Relative path where the file should be output
Template instance for type resolution
Basic Usage
public class MyFileBuilderTemplate : CSharpTemplateBase<ClassModel>, ICSharpFileBuilderTemplate
{
private readonly CSharpFile _file;
public MyFileBuilderTemplate(IOutputTarget outputTarget, ClassModel model)
: base("Template.Id", outputTarget, model)
{
_file = new CSharpFile(this.GetNamespace(), this.GetFolderPath())
.AddUsing("System")
.AddUsing("System.Collections.Generic")
.AddClass(Model.Name, @class =>
{
@class.AddConstructor();
// Configure class
});
}
public override string TransformText()
{
return _file.ToString();
}
public CSharpFile CSharpFile => _file;
}
Building Classes
Adding Classes
file.AddClass("MyClass", @class =>
{
@class
.WithBaseType("BaseClass")
.ImplementsInterface("IMyInterface")
.AddProperty("Id", "Guid", prop => prop
.WithAccessors(CSharpPropertyAccessor.Getter)
.PrivateSetter())
.AddProperty("Name", "string")
.AddConstructor(ctor => ctor
.AddParameter("id", "Guid")
.AddParameter("name", "string")
.AddStatement("Id = id;")
.AddStatement("Name = name;"))
.AddMethod("void", "DoSomething", method => method
.AddParameter("input", "string")
.AddStatement("Console.WriteLine(input);"));
});
Class Configuration
WithBaseType
Set the base class
@class.WithBaseType("BaseClass")
@class.WithBaseType("BaseClass", new[] { "TParam1", "TParam2" })
ImplementsInterface
Add interface implementations
@class.ImplementsInterface("IDisposable")
@class.ImplementsInterfaces(new[] { "IComparable", "IEquatable<T>" })
AddGenericParameter
Add generic type parameters
@class.AddGenericParameter("T")
@class.AddGenericParameter("T", out var tParam)
AddGenericTypeConstraint
Add generic type constraints
@class.AddGenericTypeConstraint("T", c => c
.AddType("IDisposable")
.AddType("new()"))
Class Members
Properties
@class.AddProperty("Name", "string", prop => prop
.WithAccessors(CSharpPropertyAccessor.Getter | CSharpPropertyAccessor.Setter)
.PrivateSetter() // Make setter private
.WithDefaultValue("\"Unknown\"")
.AddAttribute("Required"));
// Read-only property
@class.AddProperty("Id", "Guid", prop => prop
.WithAccessors(CSharpPropertyAccessor.Getter)
.WithInitializer("Guid.NewGuid()"));
// Auto-property with init
@class.AddProperty("CreatedAt", "DateTime", prop => prop
.WithAccessors(CSharpPropertyAccessor.Getter | CSharpPropertyAccessor.Init));
Fields
// Private field
@class.AddField("_logger", "ILogger", field => field
.PrivateReadOnly());
// Constant
@class.AddField("MaxItems", "int", field => field
.Constant()
.WithAssignment("100"));
// Static field
@class.AddField("Instance", "MyClass", field => field
.Static()
.PrivateReadOnly()
.WithAssignment("new MyClass()"));
Constructors
// Constructor with parameters
@class.AddConstructor(ctor => ctor
.AddParameter("logger", "ILogger", param => param
.WithDefaultValue("null"))
.AddParameter("options", "Options")
.CallsBase(ctorCall => ctorCall
.AddArgument("options.BaseValue"))
.AddStatement("_logger = logger ?? throw new ArgumentNullException(nameof(logger));"));
// Primary constructor (C# 12)
@class.WithPrimaryConstructor(primaryCtor => primaryCtor
.AddParameter("logger", "ILogger")
.AddParameter("service", "IService"));
Methods
@class.AddMethod("string", "GetDisplayName", method => method
.AddParameter("firstName", "string")
.AddParameter("lastName", "string")
.AddStatement("return $\"{firstName} {lastName}\";"));
// Async method
@class.AddMethod("Task<Result>", "ProcessAsync", method => method
.Async()
.AddParameter("input", "string")
.AddStatement("var result = await _service.ProcessAsync(input);")
.AddStatement("return result;"));
// Generic method
@class.AddMethod("T", "GetValue", method => method
.AddGenericParameter("T")
.AddGenericTypeConstraint("T", c => c.AddType("class"))
.AddParameter("key", "string")
.AddStatement("return _cache.Get<T>(key);"));
Advanced Features
Attributes
// Class attribute
@class.AddAttribute("Serializable");
@class.AddAttribute("CustomAttribute", attr => attr
.AddArgument("\"value\"")
.AddArgument("Property", "true"));
// Property attribute
prop.AddAttribute("JsonProperty", attr => attr
.AddArgument("\"custom_name\""));
// Method attribute
method.AddAttribute("HttpGet", attr => attr
.AddArgument("\"api/values/{id}\""));
XML Documentation
@class.AddMethod("void", "Process", method => method
.AddParameter("input", "string")
.WithComments(comments => comments
.AddSummary("Processes the input string")
.AddParam("input", "The string to process")
.AddReturns("The processed result")
.AddException("ArgumentNullException", "When input is null")));
Nested Types
@class.AddNestedClass("Builder", nestedClass => nestedClass
.AddProperty("Name", "string")
.AddMethod("MyClass", "Build", method => method
.AddStatement("return new MyClass(Name);")));
@class.AddNestedEnum("Status", @enum => @enum
.AddLiteral("Active")
.AddLiteral("Inactive")
.AddLiteral("Pending"));
Method Body Building
method.AddStatements("""
if (input == null)
{
throw new ArgumentNullException(nameof(input));
}
var result = await ProcessAsync(input);
return result;
""");
// Or build programmatically
method
.AddIfStatement("input == null", ifStmt => ifStmt
.AddStatement("throw new ArgumentNullException(nameof(input));"))
.AddStatement("var result = await ProcessAsync(input);")
.AddStatement("return result;");
Interfaces
file.AddInterface("IMyService", @interface => @interface
.ExtendsInterface("IDisposable")
.AddMethod("Task<Result>", "ProcessAsync", method => method
.AddParameter("input", "string"))
.AddProperty("bool", "IsReady"));
Enums
file.AddEnum("Status", @enum => @enum
.AddLiteral("None", "0")
.AddLiteral("Active", "1")
.AddLiteral("Inactive", "2")
.WithBaseType("byte")
.AddAttribute("Flags"));
Records
file.AddRecord("Person", record => record
.AddPrimaryConstructorParameter("FirstName", "string")
.AddPrimaryConstructorParameter("LastName", "string")
.AddProperty("FullName", "string", prop => prop
.WithAccessors(CSharpPropertyAccessor.Getter)
.WithInitializer("$\"{FirstName} {LastName}\""));
File Configuration
Usings
file.AddUsing("System")
.AddUsing("System.Collections.Generic")
.AddUsing("System.Linq");
// Global usings (C# 10+)
file.AddGlobalUsing("System");
Assembly Attributes
file.AddAssemblyAttribute("InternalsVisibleTo", attr => attr
.AddArgument("\"MyProject.Tests\""));
Intent Managed Modes
// Set default management mode for the file
file.IntentManagedFully(); // Mode.Fully
file.IntentManagedMerge(); // Mode.Merge
file.IntentManagedIgnore(); // Mode.Ignore
File Metadata
file.WithFileName("CustomFileName")
.WithFileExtension("g.cs")
.WithOverwriteBehaviour(OverwriteBehaviour.Always);
Complete Example
public class EntityTemplate : CSharpTemplateBase<ClassModel>, ICSharpFileBuilderTemplate
{
private readonly CSharpFile _file;
public EntityTemplate(IOutputTarget outputTarget, ClassModel model)
: base("Entity.Template", outputTarget, model)
{
_file = new CSharpFile(this.GetNamespace(), this.GetFolderPath())
.AddUsing("System")
.AddUsing("System.Collections.Generic")
.AddClass(Model.Name, @class =>
{
// Base type
if (Model.BaseType != null)
{
@class.WithBaseType(Model.BaseType.Name);
}
// Properties from model
foreach (var attribute in Model.Attributes)
{
@class.AddProperty(
attribute.Name.ToPascalCase(),
GetTypeName(attribute.TypeReference),
prop =>
{
if (attribute.HasStereotype("Required"))
{
prop.AddAttribute("Required");
}
if (attribute.HasStereotype("MaxLength"))
{
var maxLength = attribute.GetStereotypeProperty<int>(
"MaxLength", "Value");
prop.AddAttribute("MaxLength", attr => attr
.AddArgument(maxLength.ToString()));
}
});
}
// Constructor
@class.AddConstructor(ctor =>
{
foreach (var attribute in Model.Attributes)
{
ctor.AddParameter(
attribute.Name.ToCamelCase(),
GetTypeName(attribute.TypeReference));
ctor.AddStatement(
$"{attribute.Name.ToPascalCase()} = " +
$"{attribute.Name.ToCamelCase()};");
}
});
// Methods from model
foreach (var operation in Model.Operations)
{
@class.AddMethod(
GetTypeName(operation.TypeReference),
operation.Name,
method =>
{
foreach (var parameter in operation.Parameters)
{
method.AddParameter(
parameter.Name.ToCamelCase(),
GetTypeName(parameter.TypeReference));
}
method.AddStatement("throw new NotImplementedException();");
});
}
});
}
public override string TransformText()
{
return _file.ToString();
}
public CSharpFile CSharpFile => _file;
}
OnBuild and AfterBuild Hooks
File builders support configuration hooks for coordinating between decorators:// OnBuild: Configure before building
file.OnBuild(f =>
{
// Add common usings
f.AddUsing("System.Threading.Tasks");
}, order: 100);
// AfterBuild: Modify after building
file.AfterBuild(f =>
{
// Post-processing logic
}, order: 0);
Best Practices
Use Fluent API
Chain method calls for cleaner, more readable code construction
Extract Methods
Break complex class building into smaller methods for better maintainability
Use Type Resolution
Always use
GetTypeName() for resolving metadata type referencesHandle Edge Cases
Check for null references and optional elements before adding members
See Also
- Type Resolution - Resolving type references
- Code Weaving - Merging generated code with user code