Skip to main content
File Builders provide a fluent, object-oriented API for constructing code files. Instead of concatenating strings, you build an object model that generates properly formatted code.

CSharpFile

The main entry point for building C# files.

Constructor

public CSharpFile(string @namespace, string relativeLocation)
public CSharpFile(string @namespace, string relativeLocation, ICSharpFileBuilderTemplate template)
namespace
string
The namespace for the file
relativeLocation
string
Relative path where the file should be output
template
ICSharpFileBuilderTemplate
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 references

Handle Edge Cases

Check for null references and optional elements before adding members

See Also

Build docs developers (and LLMs) love