Houtamelo.Spire 4.7.0

dotnet add package Houtamelo.Spire --version 4.7.0
                    
NuGet\Install-Package Houtamelo.Spire -Version 4.7.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="Houtamelo.Spire" Version="4.7.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Houtamelo.Spire" Version="4.7.0" />
                    
Directory.Packages.props
<PackageReference Include="Houtamelo.Spire" />
                    
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 Houtamelo.Spire --version 4.7.0
                    
#r "nuget: Houtamelo.Spire, 4.7.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 Houtamelo.Spire@4.7.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=Houtamelo.Spire&version=4.7.0
                    
Install as a Cake Addin
#tool nuget:?package=Houtamelo.Spire&version=4.7.0
                    
Install as a Cake Tool

Houtamelo.Spire

Roslyn analyzers and source generators for C# type safety — struct initialization enforcement, discriminated unions, and exhaustive switch checking.

Installation

Just add the NuGet package:

dotnet add package Houtamelo.Spire

Rules

Rule Description
SPIRE001 Non-empty array of [EnforceInitialization] struct produces default instances
SPIRE002 [EnforceInitialization] on fieldless type has no effect
SPIRE003 default(T) where T is an [EnforceInitialization] type
SPIRE004 new T() on [EnforceInitialization] struct without parameterless constructor
SPIRE005 Activator.CreateInstance on [EnforceInitialization] struct
SPIRE006 Clearing array or span of [EnforceInitialization] type
SPIRE007 Unsafe.SkipInit on [EnforceInitialization] struct
SPIRE008 RuntimeHelpers.GetUninitializedObject on [EnforceInitialization] struct
SPIRE009 Switch does not handle all variants of discriminated union
SPIRE010 Expand wildcard to explicit variant arms (refactoring)
SPIRE011 Discriminated union pattern field type mismatch
SPIRE012 Discriminated union pattern field count mismatch
SPIRE013 Accessing another variant's field
SPIRE014 Accessing variant field without kind guard
SPIRE015 Exhaustive enum switch
SPIRE016 Cast to [EnforceInitialization] enum may produce invalid variant

Discriminated Unions

The source generator provides:

  • Kind enum and kind property for variant discrimination.
  • Factory methods (Shape.Circle(5.0)) and IsVariant properties (shape.IsCircle).
  • Property-pattern and Deconstruct support for switch.
  • Optional JSON serialization (System.Text.Json, Newtonsoft.Json).
  • Exhaustive switch checking (SPIRE009) and field access safety (SPIRE011-014).
  • 5 struct memory layouts + record unions.

Define a struct union with [DiscriminatedUnion]:

using Houtamelo.Spire;

[DiscriminatedUnion(Layout.Additive)]
partial struct Shape
{
    [Variant] public static partial Shape Circle(double radius);
    [Variant] public static partial Shape Square(int side);
    [Variant] public static partial Shape Point();
}

Construct and match:

Shape shape = Shape.Circle(5.0);

// Property pattern
var area = shape switch
{
    { kind: Shape.Kind.Circle, radius: var r } => Math.PI * r * r,
    { kind: Shape.Kind.Square, side: var s }   => s * s,
    { kind: Shape.Kind.Point }                 => 0,
};

// Deconstruct pattern
area = shape switch 
{
    (Shape.Kind.Circle, radius: var r) => Math.PI * r * r,
    (Shape.Kind.Square, side: var s)   => s * s,
    (Shape.Kind.Point, _)              => 0,
};

Record types are also supported:

[DiscriminatedUnion]
partial abstract record Shape
{
    public partial record Circle(double radius) : Shape;
    public partial record Square(int side) : Shape;
    public partial record Point() : Shape;
}

See docs/discriminated-unions.md for the full guide.

Global Configuration

Set project-wide defaults via MSBuild properties in your .csproj (or Directory.Build.props):

<PropertyGroup>
  
  <Spire_DU_DefaultLayout>Additive</Spire_DU_DefaultLayout>
  <Spire_DU_DefaultGenerateDeconstruct>true</Spire_DU_DefaultGenerateDeconstruct>
  <Spire_DU_DefaultJson>SystemTextJson</Spire_DU_DefaultJson>
  <Spire_DU_DefaultJsonDiscriminator>kind</Spire_DU_DefaultJsonDiscriminator>

  
  <Spire_EnforceExhaustivenessOnAllEnumTypes>false</Spire_EnforceExhaustivenessOnAllEnumTypes>
</PropertyGroup>
Property Values Default
Spire_DU_DefaultLayout Auto, Additive, Overlap, UnsafeOverlap, BoxedFields, BoxedTuple Auto
Spire_DU_DefaultGenerateDeconstruct true, false true
Spire_DU_DefaultJson None, SystemTextJson, NewtonsoftJson, Both None
Spire_DU_DefaultJsonDiscriminator any string kind
Spire_EnforceExhaustivenessOnAllEnumTypes true, false false

Per-attribute values override global defaults. Omitting an attribute parameter (or using ReadGlobalCfg) falls through to the global config.

EnforceExhaustiveness

The analyzer provides:

  • Exhaustive switch checking over enum members or sealed subtypes (SPIRE015).
  • Automatic suppression of CS8509 / CS8524 for switches Spire's checker proves exhaustive — including tuples of enums, nullable enums, and unmarked enums covering all named members. No opt-in required.
  • Invalid enum cast detection (SPIRE016).
  • Inherits all [EnforceInitialization] rules (SPIRE001-008) — prevents default(T), uninitialized arrays, etc.
  • Works on enums, abstract classes, and interfaces.

Apply [EnforceExhaustiveness] to a type to require exhaustive switches:

using Houtamelo.Spire;

[EnforceExhaustiveness]
public abstract class Animal { }
public sealed class Dog : Animal { }
public sealed class Cat : Animal { }

// SPIRE015: switch does not handle 'Cat'
string Describe(Animal a) => a switch
{
    Dog => "dog",
};

See docs/exhaustiveness.md for the full guide.

Discriminated Unions — Layout Strategy Comparison

N=1000, .NET 11.0, AMD Ryzen 9 9900X. Full results in docs/benchmark-results/.

Legend: Ctor = Construction, Prop = Property Pattern, Decon = Deconstruct

Layout Ctor Speed Ctor Alloc Prop Match Decon Match Decon Alloc Copy Generics
Additive Fast Moderate Fastest Slow (boxing) Yes Slow (large) Yes
Overlap Fast Moderate Fastest Slow (boxing) Yes Slow (large) No
UnsafeOverlap Moderate Moderate Fastest Slow (boxing) Yes Slow (large) No
BoxedTuple Fast Low Slow (cast) Fast None Fast (16B) Yes
BoxedFields Slow High Slowest Fast None Slowest Yes
Record Fastest Low Fast Fastest None Fastest (8B) Yes
Native (C# 15) Fast Low (heap) Moderate Moderate None Fast (ref) Yes

Choosing a layout

  • Default choice: Layout.Additive — fastest struct match, zero-alloc property access, works with generics.
  • Reference semantics or JSON: record — fastest construction, best JSON deserialization, natural C# pattern matching. Trades GC pressure for speed.
  • No generics, unmanaged-heavy: Layout.Overlap — true field overlap for minimal struct size. Cannot handle generic type parameters.
  • Skewed variant distribution (80%+ one variant): Layout.BoxedTuple — fieldless fast-path is sub-nanosecond. Poor uniform performance.
  • C# 15 native unions: comparable to Additive/Record depending on usage; avoids source generator overhead.
  • Avoid: Layout.BoxedFields — outperformed by Additive in most categories. Only advantage: zero-alloc Deconstruct (fields already boxed).

License

MIT

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in 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
4.7.0 103 4/17/2026
4.5.0 605 4/7/2026
4.4.0 99 4/7/2026
4.3.0 136 4/3/2026
4.2.0 102 4/2/2026
4.1.0 111 3/31/2026
4.0.0 113 3/31/2026
3.0.0 105 3/30/2026
2.0.0 88 3/26/2026