Hertzole.SourceGenUtils 1.2.0

Prefix Reserved
dotnet add package Hertzole.SourceGenUtils --version 1.2.0
                    
NuGet\Install-Package Hertzole.SourceGenUtils -Version 1.2.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Hertzole.SourceGenUtils" Version="1.2.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Hertzole.SourceGenUtils" Version="1.2.0" />
                    
Directory.Packages.props
<PackageReference Include="Hertzole.SourceGenUtils" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Hertzole.SourceGenUtils --version 1.2.0
                    
#r "nuget: Hertzole.SourceGenUtils, 1.2.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Hertzole.SourceGenUtils@1.2.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Hertzole.SourceGenUtils&version=1.2.0
                    
Install as a Cake Addin
#tool nuget:?package=Hertzole.SourceGenUtils&version=1.2.0
                    
Install as a Cake Tool

SourceGen Utils

C# incremental source generator that emits common utility types directly into your assembly. Zero runtime dependencies, zero unused code - only what you call gets generated.

Features

  • Only include what you use - methods you never call are never generated
  • Zero runtime dependencies - everything is emitted as source into your project, no external library references needed
  • Code writer - a StringBuilder-based code writer with automatic indentation and append methods built for source generation
  • Performant - heavy use of spans and pools in generated code

Installation

NuGet

dotnet add package Hertzole.SourceGenUtils

Package Reference


<PackageReference Include="Hertzole.SourceGenUtils" Version="*"/>

Generated Utilities

All types are emitted under the Hertzole.SourceGen namespace.

CodeWriter

A StringBuilder-based code writer with automatic indentation and a fluent API.

// Must be disposed to return string buffers
using var writer = new CodeWriter();
writer
    // Wraps your code in a namespace with automatic indentation
    .AppendNamespace("MyApp.Models")
    .AppendLine("public class User");

// Wraps your code in a block ({ }) with automatic indentation
using (writer.WithBlock())
{
    writer.AppendLine("public string Name { get; set; }");
    writer.AppendLine("public string GetName()");
    using (writer.WithBlock())
    {
        writer.AppendLine("if (string.IsNullOrEmpty(Name))");
        // Indents 4 spaces
        writer.Indent++;
        writer.AppendLine("return \"No name\"");
        // Indents back
        writer.Indent--;
        writer.AppendLine("return Name");
    }

    // Append complete source blocks
    // All indents are handled automatically
    writer.AppendIndentedSource("""
                                public void CompleteCode()
                                {
                                    // Proper indents!
                                }
                                """)
}

// Use ToString() to get the final result
Console.WriteLine(writer.ToString());

Supports all the built-in types, appending the current line, and creating new lines. Also has helper methods for appending common operations, like namespaces, [GeneratedCode] attribute, preprocessor symbols, and more.

ObjectPool<T>

Stack-based object pool with factory and lifecycle callbacks (onGet, onReturn, onDispose).

// Pool should be disposed when no longer used
using var pool = new ObjectPool<StringBuilder>(
    create: () => new StringBuilder(),
    onGet: sb => sb.Clear(),
    onReturn: sb => { }
);

// Gets from the pool
var sb = pool.Get();

// Return to pool
pool.Return(sb);

// Use a scope to automatically return items
using (var scope = pool.Get(out var sb))
{
    sb.Append("hello");
}

Collection Pools

Static pools for common collections. All follow the same Get() / Get(out T) / Return() pattern with automatic clearing on return.

Type Wraps
StringBuilderPool StringBuilder (initial capacity 1024)
ListPool<T> List<T>
HashSetPool<T> HashSet<T>
StackPool<T> Stack<T>
QueuePool<T> Queue<T>
using var list = ListPool<int>.Get(out var items);
items.Add(42);
// returned and cleared on dispose

EquatableArray<T>

readonly struct providing value-based equality for arrays.

var a = new EquatableArray<string>(new[] { "hello", "world" });
var b = new EquatableArray<string>(new[] { "hello", "world" });
Console.WriteLine(a == b); // True

ArrayBuilder<T>

Pool-backed array builder using ArrayPool<T>.Shared. Supports Add, AddRange, Remove, RemoveAt, and Clear. More performant than List<T> when you just need to quickly construct arrays.

Bonus! Calling ToString() on ArrayBuilder<char> returns the built string, like a StringBuilder.

using var builder = new ArrayBuilder<int>();
builder.Add(1);
builder.Add(2);
builder.AddRange(stackalloc[] { 3, 4 });

int[] array = builder.ToArray();
ImmutableArray<int> immutable = builder.ToImmutable();
ReadOnlySpan<char> span = builder.AsSpan();

// buffer returned to pool on dispose

VariableNames

Variable name transformation utilities for code generation. Nicify, remove prefixes (m_, _, k), uppercase start, and detect event handler patterns. All, of course, support spans along with simply returning strings.

VariableNames.NicifyVariableName("m_playerName"); // "PlayerName"
VariableNames.RemovePrefix("kConstant");          // "Constant"
VariableNames.UppercaseStart("playerPoints");     // "PlayerPoints"
VariableNames.AppendGlobalPrefix("MyType");       // "global::MyType"

StringBuilderExtensions

Extensions for StringBuilder to append read-only character spans.

ReadOnlySpan<char> span = "Hello world! Secret message".AsSpan().Slice(0, 12);
StringBuilder builder = new StringBuilder();
builder.Append(span);
builder.AppendLine(span);

SymbolExtensions

Symbol helper methods for various tasks.

ITypeSymbol structSymbol = ...; // for struct
ITypeSymbol staticClassSymbol = ...; // for static class
bool isPartial = false;
structSymbol.GetDeclarationString(isPartial);      // struct 
staticClassSymbol.GetDeclarationString(isPartial); // static class

ISymbol symbol ...;
symbol.HasAttribute("My.Namespace.MyAttribute");   // true or false

if (symbol.TryGetAttribute("My.Attribute", out AttributeData? attribute))
{
    // Found attribute, do something with it.
}

SyntaxExtensions

Roslyn syntax helpers to resolve attribute symbols and find field declarations from nested syntax nodes.

Log

Debug-only file-based logging (#if DEBUG). Writes timestamped entries to {AssemblyName}.log.

You don't need to put them in a #if DEBUG preprocessor symbol, they simply just get removed in release builds!

Log.Info("Generation started");
Log.Warning("Unsupported syntax node");
Log.Error("Failed to resolve symbol");

Assembly attributes

There are two attributes to spare you the hassle of manually injecting support for records and required members.

using Hertzole.SourceGen;

// For enabling support for C# records
[assembly: EnableRecordSupport]
// For enabling support for the 'required' keyword
[assembly: EnableRequiredSupport]

How It Works

The generator operates in two phases:

  1. Shell generation - emits partial type stubs so your code can reference the utilities during compilation.
  2. Incremental implementation - scans for invocations and object creation expressions matching known types. Only called methods get full implementations. Uncalled methods compile as empty stubs.

This means adding the NuGet package has a minimal impact on your assembly size until you actually use a utility. When nothing is used, it adds roughly 11 kb to your assembly.

Requirements

  • .NET Standard 2.0+ (source generator target)
  • Roslyn 4.x+

License

MIT

There are no supported framework assets in this package.

Learn more about Target Frameworks and .NET Standard.

  • .NETStandard 2.0

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.2.0 106 9/5/2026
1.1.0 123 8/1/2026