BB84.SourceGenerators 1.4.415

There is a newer version of this package available.
See the version list below for details.
dotnet add package BB84.SourceGenerators --version 1.4.415
                    
NuGet\Install-Package BB84.SourceGenerators -Version 1.4.415
                    
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="BB84.SourceGenerators" Version="1.4.415">
  <PrivateAssets>all</PrivateAssets>
  <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="BB84.SourceGenerators" Version="1.4.415" />
                    
Directory.Packages.props
<PackageReference Include="BB84.SourceGenerators">
  <PrivateAssets>all</PrivateAssets>
  <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
                    
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 BB84.SourceGenerators --version 1.4.415
                    
#r "nuget: BB84.SourceGenerators, 1.4.415"
                    
#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 BB84.SourceGenerators@1.4.415
                    
#: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=BB84.SourceGenerators&version=1.4.415
                    
Install as a Cake Addin
#tool nuget:?package=BB84.SourceGenerators&version=1.4.415
                    
Install as a Cake Tool

BB84.SourceGenerators

A collection of C# source generators that automatically generate boilerplate code at compile time, reducing manual coding and improving code maintainability.

CI CD CodeQL Dependabot

License: MIT C# .NET Standard 2.0 Issues LastCommit PullRequests RepoSize NuGet

Features

This package provides eight powerful source generators:

  • Enumerator Extensions Generator - Fast, allocation-free extension methods for enums
  • Notification Properties Generator - Automatic INotifyPropertyChanged/INotifyPropertyChanging implementation
  • Abstraction Generator - Interface and implementation generation for static classes
  • INI File Generator - Compile-time INI file serialization and deserialization
  • Builder Generator - Fluent builder pattern generation for classes
  • ToString Generator - Compile-time ToString() override generation
  • Validator Generator - Compile-time data annotation validation
  • Equality Generator - Compile-time Equals, GetHashCode, and operator generation

Installation

Install the package via NuGet:

dotnet add package BB84.SourceGenerators

Or via Package Manager Console:

Install-Package BB84.SourceGenerators

Usage

1. Enumerator Extensions Generator

Generates high-performance extension methods for enumerations, providing faster alternatives to Enum.ToString(), Enum.IsDefined(), Enum.GetNames(), and Enum.GetValues().

Attribute
[GenerateEnumeratorExtensions]
Example
using BB84.SourceGenerators.Attributes;

[GenerateEnumeratorExtensions]
public enum Status
{
    [System.ComponentModel.Description("Pending approval")]
    Pending = 0,
    Active = 1,
    [System.ComponentModel.Description("Temporarily inactive")]
    Inactive = 2,
    Deleted = 3
}
Generated Methods

The generator creates the following extension methods:

  • ToStringFast() - Returns the name of the enum value as a string
  • IsDefinedFast(this TEnum value) - Checks if an enum value is defined
  • IsDefinedFast(this string name) - Checks if an enum name is defined
  • GetNamesFast() - Returns all enum names as an IEnumerable<string>
  • GetValuesFast() - Returns all enum values as an IEnumerable<TEnum>
  • GetDescriptionFast() - Returns the description from [Description] attribute, or the name if not present
Usage Example
var status = Status.Pending;

// Fast string conversion
string name = status.ToStringFast(); // "Pending"

// Check if defined
bool isDefined = status.IsDefinedFast(); // true
bool isNameDefined = "Active".IsDefinedFast(); // true

// Get description
string description = status.GetDescriptionFast(); // "Pending approval"

// Get all names and values
IEnumerable<string> names = status.GetNamesFast();
IEnumerable<Status> values = status.GetValuesFast();

2. Notification Properties Generator

Automatically generates properties with INotifyPropertyChanged and INotifyPropertyChanging support for private fields in a class.

Attribute
[GenerateNotifications(bool isChanged = false)]

Parameters:

  • isChanged - When true, generates an additional IsChanged boolean property that is set to true when any property changes
Example
using BB84.SourceGenerators.Attributes;

[GenerateNotifications(isChanged: true)]
public partial class Person
{
    private int _id;
    private string _name;
    private string _email;
    private DateTime _createdAt;
    private DateTime? _updatedAt;

    public Person(int id, string name, string email)
    {
        _id = id;
        _name = name;
        _email = email;
        _createdAt = DateTime.UtcNow;
    }
}
Generated Code

The generator creates:

  • Public properties for each private field with change notification
  • Implementation of INotifyPropertyChanged and INotifyPropertyChanging interfaces
  • PropertyChanged and PropertyChanging events
  • RaisePropertyChanged() and RaisePropertyChanging() protected virtual methods
  • Optional IsChanged property (when isChanged parameter is true)
Usage Example
var person = new Person(1, "John Doe", "john@example.com");

// Subscribe to change notifications
person.PropertyChanging += (s, e) => Console.WriteLine($"Property {e.PropertyName} is changing");
person.PropertyChanged += (s, e) => Console.WriteLine($"Property {e.PropertyName} has changed");

// Change a property - events will fire automatically
person.Name = "Jane Doe";
person.Email = "jane@example.com";

// Check if object has been modified (when isChanged: true)
if (person.IsChanged)
{
    Console.WriteLine("Person has been modified");
}

3. Abstraction Generator

Generates interface and implementation classes for static classes, making them testable through dependency injection.

Attribute
[GenerateAbstraction(Type targetType, Type abstractionType, Type implementationType, params string[] excludeMethods)]

Parameters:

  • targetType - The static class to generate an abstraction for
  • abstractionType - The interface type to generate
  • implementationType - The implementation class type to generate
  • excludeMethods - Optional array of method names to exclude from generation
Example
using BB84.SourceGenerators.Attributes;

// Generate abstraction for System.IO.File
[GenerateAbstraction(typeof(File), typeof(IFileProvider), typeof(FileProvider))]
public partial class FileProvider
{ }

public partial interface IFileProvider
{ }
Generated Code

The generator creates:

  • A complete interface (IFileProvider) with all public static methods from the target type
  • An implementation class (FileProvider) that implements the interface and delegates to the static class
  • XML documentation comments using <inheritdoc>
Usage Example
// In your service
public class DocumentService
{
    private readonly IFileProvider _fileProvider;

    public DocumentService(IFileProvider fileProvider)
        => _fileProvider = fileProvider;

    public string ReadDocument(string path)
        => _fileProvider.ReadAllText(path);
}

// In your DI container setup
services.AddSingleton<IFileProvider, FileProvider>();

// In tests, you can mock IFileProvider
var mockFileProvider = new Mock<IFileProvider>();
mockFileProvider.Setup(x => x.ReadAllText(It.IsAny<string>())).Returns("test content");

4. INI File Generator

Generates static Read and Write methods for classes, enabling compile-time INI file serialization and deserialization based on decorated properties.

Attributes
[GenerateIniFile]
[GenerateIniFileSection(string? name = null)]
[GenerateIniFileValue(string? name = null)]

Parameters:

  • GenerateIniFile - Marks a class for INI file code generation
  • GenerateIniFileSection - Marks a property as an INI file section. The optional name parameter specifies the section name; if omitted, the property name is used
  • GenerateIniFileValue - Marks a property as a key-value pair within an INI section. The optional name parameter specifies the key name; if omitted, the property name is used

Supported Value Types: string, int, long, float, double, bool, decimal, DateTime

Example
using BB84.SourceGenerators.Attributes;

[GenerateIniFile]
public partial class AppConfig
{
    [GenerateIniFileSection("General")]
    public GeneralSection General { get; set; }

    [GenerateIniFileSection("Database")]
    public DatabaseSection Database { get; set; }
}

public class GeneralSection
{
    [GenerateIniFileValue("AppName")]
    public string AppName { get; set; }

    [GenerateIniFileValue("Version")]
    public int Version { get; set; }

    [GenerateIniFileValue("Debug")]
    public bool Debug { get; set; }
}

public class DatabaseSection
{
    [GenerateIniFileValue("Server")]
    public string Server { get; set; }

    [GenerateIniFileValue("Port")]
    public int Port { get; set; }

    [GenerateIniFileValue("Timeout")]
    public double Timeout { get; set; }
}
Generated Methods

The generator creates the following static methods on the decorated class:

  • Read(string content) - Parses an INI file string and returns a deserialized instance
  • Write(TClass instance) - Serializes an instance into an INI file string
Usage Example
// Reading an INI file
string iniContent = File.ReadAllText("config.ini");
AppConfig config = AppConfig.Read(iniContent);

Console.WriteLine(config.General.AppName); // "MyApp"
Console.WriteLine(config.Database.Port);   // 5432

// Modifying and writing back
config.General.Debug = false;
config.Database.Timeout = 60.0;

string output = AppConfig.Write(config);
File.WriteAllText("config.ini", output);

Output:

[General]
AppName=MyApp
Version=1
Debug=False

[Database]
Server=localhost
Port=5432
Timeout=60

5. Builder Generator

Generates a fluent builder class for classes, providing With{PropertyName}(value) methods for each public settable property and a Build() method that creates the instance.

Attribute
[GenerateBuilder]
Example
using BB84.SourceGenerators.Attributes;

[GenerateBuilder]
public partial class UserProfile
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string? Email { get; set; }
    public int Age { get; set; }
    public bool IsActive { get; set; }
}
Generated Code

The generator creates a {ClassName}Builder class with:

  • A With{PropertyName}(value) fluent method for each public settable property
  • A Build() method that creates the instance via object initializer
  • Proper nullable reference type annotations
  • XML documentation comments
Usage Example
// Create an instance using the fluent builder
UserProfile profile = new UserProfileBuilder()
    .WithId(1)
    .WithName("John Doe")
    .WithEmail("john@example.com")
    .WithAge(30)
    .WithIsActive(true)
    .Build();

// Only set the properties you need - others use default values
UserProfile minimal = new UserProfileBuilder()
    .WithName("Jane Doe")
    .Build();

// Builders can be reused to create multiple instances
var builder = new UserProfileBuilder()
    .WithName("Template User")
    .WithIsActive(true);

UserProfile first = builder.WithId(1).Build();
UserProfile second = builder.WithId(2).Build();

6. ToString Generator

Generates a ToString() override for classes, returning a formatted string containing the class name and all (or selected) public readable property values in the format ClassName { Prop1 = val1, Prop2 = val2 }.

Attribute
[GenerateToString(params string[] excludeProperties)]

Parameters:

  • excludeProperties - Optional list of property names to exclude from the generated ToString() output
Example
using BB84.SourceGenerators.Attributes;

[GenerateToString]
public partial class Product
{
    public int Id { get; set; }
    public string Name { get; set; }
    public double Price { get; set; }
    public bool InStock { get; set; }
}

// Exclude sensitive or verbose properties
[GenerateToString("PasswordHash", "InternalNotes")]
public partial class User
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string PasswordHash { get; set; }
    public string InternalNotes { get; set; }
}
Generated Code

The generator creates a ToString() override on the partial class that:

  • Includes all public readable properties by default
  • Excludes properties specified in the attribute parameter
  • Formats output as ClassName { Prop1 = val1, Prop2 = val2 }
  • Returns ClassName { } when all properties are excluded
Usage Example
var product = new Product
{
    Id = 1,
    Name = "Widget",
    Price = 9.99,
    InStock = true
};

Console.WriteLine(product.ToString());
// Output: Product { Id = 1, Name = Widget, Price = 9.99, InStock = True }

var user = new User
{
    Id = 42,
    Name = "John Doe",
    PasswordHash = "abc123hash",
    InternalNotes = "VIP customer"
};

Console.WriteLine(user.ToString());
// Output: User { Id = 42, Name = John Doe }

7. Validator Generator

Generates a Validate() method for classes, scanning properties for data annotation attributes at compile time and emitting direct validation checks. This replaces runtime reflection-based Validator.TryValidateObject() with zero-overhead generated code.

Attribute
[GenerateValidator]

Supported Data Annotations:

  • [Required] - Validates that the property is not null (or not null/empty for strings)
  • [Range(min, max)] - Validates that a numeric value falls within a specified range, or that a collection has between min and max elements
  • [StringLength(max, MinimumLength = min)] - Validates string length within bounds
  • [MinLength(length)] - Validates minimum length of a string or collection
  • [MaxLength(length)] - Validates maximum length of a string or collection
  • [RegularExpression(pattern)] - Validates that a string matches a regex pattern
Example
using System.ComponentModel.DataAnnotations;
using BB84.SourceGenerators.Attributes;

[GenerateValidator]
public partial class UserRegistration
{
    [Required]
    [StringLength(100, MinimumLength = 2)]
    public string? Name { get; set; }

    [Required]
    [RegularExpression(@"^[^@\s]+@[^@\s]+\.[^@\s]+$")]
    public string? Email { get; set; }

    [Range(18, 120)]
    public int Age { get; set; }

    [Required]
    [MinLength(8)]
    [MaxLength(128)]
    public string? Password { get; set; }

		[Range(1, 10)]
		public List<int>? Skills { get; set; }
}
Generated Code

The generator creates a Validate() method on the partial class that:

  • Returns a List<string> of validation error messages
  • Contains direct if-checks for each data annotation rule
  • Supports custom error messages via ErrorMessage property
  • Returns an empty list when the instance is valid
Usage Example
var registration = new UserRegistration
{
    Name = "J",
    Email = "not-an-email",
    Age = 15,
    Password = "short"
};

List<string> errors = registration.Validate();

if (errors.Count > 0)
{
    foreach (string error in errors)
    {
        Console.WriteLine(error);
    }
    // Output:
    // The field Name must be a string with a minimum length of 2 and a maximum length of 100.
    // The field Email must match the regular expression '^[^@\s]+@[^@\s]+\.[^@\s]+$'.
    // The field Age must be between 18 and 120.
    // The field Password must be a string or collection with a minimum length of 8.
}
else
{
    Console.WriteLine("Registration is valid!");
}

// Custom error messages
[GenerateValidator]
public partial class LoginModel
{
    [Required(ErrorMessage = "Username is required.")]
    public string? Username { get; set; }

    [Required(ErrorMessage = "Password cannot be empty.")]
    [MinLength(6, ErrorMessage = "Password must be at least 6 characters.")]
    public string? Password { get; set; }
}

8. Equality Generator

Generates Equals(object), Equals(T), GetHashCode(), operator ==, and operator != for classes, implementing IEquatable<T>. This replaces tedious and error-prone manual equality implementations with zero-overhead generated code.

Attribute
[GenerateEquality(params string[] excludeProperties)]

Parameters:

  • excludeProperties - Optional list of property names to exclude from the generated equality comparison
Example
using BB84.SourceGenerators.Attributes;

[GenerateEquality]
public partial class Product
{
    public int Id { get; set; }
    public string? Name { get; set; }
    public double Price { get; set; }
    public bool InStock { get; set; }
}

// Exclude volatile or non-significant properties
[GenerateEquality("CachedHash", "LastAccessed")]
public partial class User
{
    public int Id { get; set; }
    public string? Name { get; set; }
    public string? CachedHash { get; set; }
    public DateTime LastAccessed { get; set; }
}
Generated Code

The generator creates the following members on the partial class:

  • Equals(object?) override — delegates to the typed Equals(T?) method
  • Equals(T?) implementing IEquatable<T> — compares all (or selected) public properties
  • GetHashCode() override — produces a consistent hash from all (or selected) public properties
  • operator == and operator != — delegates to Equals
Usage Example
var a = new Product { Id = 1, Name = "Widget", Price = 9.99, InStock = true };
var b = new Product { Id = 1, Name = "Widget", Price = 9.99, InStock = true };
var c = new Product { Id = 2, Name = "Gadget", Price = 19.99, InStock = false };

// Typed equality
bool equal = a.Equals(b);    // true
bool different = a.Equals(c); // false

// Operator equality
bool op = a == b;  // true
bool neq = a != c; // true

// Consistent hash codes
bool sameHash = a.GetHashCode() == b.GetHashCode(); // true

// Works with collections that use equality
var set = new HashSet<Product> { a };
bool contains = set.Contains(b); // true

// IEquatable<T> is implemented
IEquatable<Product> equatable = a;

Requirements

  • .NET Standard 2.0 or higher
  • C# 7.3 or higher
  • Supports .NET Framework 4.7.2+, .NET Core 2.0+, .NET 5+, .NET 6+, .NET 7+, .NET 8+

Performance Benefits

Enum Extensions

The generated enum extension methods provide significant performance improvements over reflection-based Enum methods:

  • ToStringFast() - Avoids boxing and uses switch expressions
  • IsDefinedFast() - Compile-time switch instead of runtime reflection
  • GetNamesFast()/GetValuesFast() - Returns pre-allocated arrays instead of reflection

Notification Properties

  • Generates optimized property setters with inline equality checks
  • Avoids reflection overhead of PropertyChanged.Fody or similar tools
  • Compile-time code generation means zero runtime overhead

INI File Serialization

  • Generates direct string parsing and formatting code at compile time
  • Avoids runtime reflection or third-party INI parsing libraries
  • Uses CultureInfo.InvariantCulture for consistent cross-platform formatting

Builder Pattern

  • Generates a complete fluent builder class at compile time
  • Eliminates hand-written builder boilerplate that must be kept in sync with the target class
  • Replaces reflection-based or expression-tree-based builder libraries with zero-overhead generated code
  • Full nullable reference type support for type-safe builder APIs

ToString Generation

  • Generates a direct property-formatting ToString() override at compile time
  • Replaces runtime reflection approaches (typeof(T).GetProperties().Select(...)) with zero-overhead generated code
  • Automatically stays in sync with the class definition - no manual maintenance required
  • Supports property exclusion for sensitive or verbose fields

Validation

  • Generates direct if-checks at compile time for each data annotation rule
  • Replaces Validator.TryValidateObject() which uses TypeDescriptor and reflection at runtime
  • Provides compile-time discovery of validation attributes - no runtime attribute scanning
  • Supports custom error messages for localization and user-friendly feedback

Equality

  • Generates correct Equals, GetHashCode, ==, and != implementations at compile time
  • Eliminates tedious and error-prone manual equality boilerplate
  • Replaces runtime reflection approaches with zero-overhead generated code
  • Properly handles null references and value type properties
  • Supports property exclusion for volatile or non-significant fields

How It Works

Source generators run during compilation and generate additional C# source files that are compiled alongside your code. This means:

  • Zero runtime overhead - All code is generated at compile time
  • IntelliSense support - Generated code appears in Visual Studio IntelliSense
  • Debuggable - You can step through generated code during debugging
  • No reflection - Generated code uses direct method calls

Contributing

Contributions are welcome! Please feel free to submit a Pull Request but see the Conduct first.

License

This project is licensed under the MIT License - see the LICENSE file for details.

Author

Robert Peter Meyer (BoBoBaSs84)

Changelog

See releases for version history and changelog.

There are no supported framework assets in this package.

Learn more about Target Frameworks and .NET Standard.

This package has 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
2.2.614 220 6/14/2026
2.1.607 237 6/7/2026
2.0.529 165 5/29/2026
2.0.526 169 5/26/2026
2.0.524 123 5/24/2026
1.11.515 163 5/15/2026
1.10.507 155 5/7/2026
1.9.503 153 5/3/2026
1.9.430 128 4/30/2026
1.9.428 122 4/28/2026
1.8.426 141 4/26/2026
1.7.421 131 4/21/2026
1.6.419 121 4/19/2026
1.5.417 110 4/17/2026
1.4.416 109 4/16/2026
1.4.415 112 4/15/2026
1.3.413 128 4/13/2026
1.3.412 119 4/12/2026
1.2.410 130 4/10/2026
1.2.409.1 108 4/9/2026
Loading failed