Coplt.Union 0.16.0

dotnet add package Coplt.Union --version 0.16.0                
NuGet\Install-Package Coplt.Union -Version 0.16.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="Coplt.Union" Version="0.16.0" />                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add Coplt.Union --version 0.16.0                
#r "nuget: Coplt.Union, 0.16.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.
// Install Coplt.Union as a Cake Addin
#addin nuget:?package=Coplt.Union&version=0.16.0

// Install Coplt.Union as a Cake Tool
#tool nuget:?package=Coplt.Union&version=0.16.0                

Coplt.Union

.NET MIT

  • Sera.Union
    Nuget
  • Sera.Union.Utilities
    Nuget
    Includes Option<T> and Result<T, E>

Generate Tagged Union using source generator

  • Source distribution, no runtime dependencies
  • All unmanaged types will overlap
  • All classes will overlap
  • Other types are sequential
  • Support generics, but generics cannot overlap

Usage

[Union]
public readonly partial struct Union1
{
    // This template is not used at runtime, its only purpose is to provide type symbols to the roslyn analyzer
    [UnionTemplate]
    private interface Template
    {
        int A();
        string B();
        bool C();
        (int a, int b) D();
        void E();               // Tag only variant
        List<int>? F();
        (int a, string b) G();
        // Record mode, in this mode, multiple fields are stored separately.
        void H(int a, int b, string c, HashSet<int> d, (int a, string b) e);
    }
}

Will generate (pseudocode):

public readonly partial struct Union1
{
    private readonly __impl_ _impl;
    
    // If the first item is a Tag only, it starts at 0, otherwise it starts at 1
    // Use [UnionTag(value)] to explicitly mark enum values
    public enum Tags : byte { A = 1, B, C, D, E, F, G, H }
    
    private struct __impl_
    {
        public object? _c0;                 // All classes will overlap
        public object? _c1;                 // The second reference type is used by variant H, record mode allows multiple types
        public __unmanaged_ _u;             // All unmanaged types will overlap
        public (int a, string b) _f0_0;     // Mixed types cannot overlap
        public readonly Tags _tag;
        
        [StructLayout(LayoutKind.Explicit)] internal struct __unmanaged_
        {
            [FieldOffset(0)] public int  _0;
            [FieldOffset(0)] public bool _1;
            [FieldOffset(0)] public (int a, int b) _2;
            [FieldOffset(0)] public __record_7_unmanaged_ _3;
        }
        
        // The fields of the record mode variant are stored separately, with the unmanaged part having a separate structure
        internal struct __record_7_unmanaged_
        {
            public int _0;
            public int _1;
        }
    }
    
    public static Union1 MakeA(int value) { ... }
    public static Union1 MakeB(string value) { ... }
    public static Union1 MakeC(bool value) { ... }
    public static Union1 MakeD((int a, int b) value) { ... }
    public static Union1 MakeE() { ... }
    public static Union1 MakeF(List<int>? value) { ... }
    public static Union1 MakeG((int a, string b) value) { ... }
    public static Union1 MakeH(int a, int b, string c, HashSet<int> d, (int a, string b) e) { ... }
    
    public readonly Tags Tag { get; }
    public readonly bool IsA { get; }
    public readonly bool IsB { get; }
    public readonly bool IsC { get; }
    public readonly bool IsD { get; }
    public readonly bool IsE { get; }
    public readonly bool IsF { get; }
    public readonly bool IsG { get; }
    public readonly bool IsH { get; }
    
    // ref readonly if sturct is readonly, otherwise ref only
    // If the current tag does not match, a null reference will be returned
    public ref readonly int A  => ref _impl._u._0;
    public ref readonly string B  => ref Unsafe.As<object?, string>(ref _impl._c0);
    public ref readonly bool C  => ref _impl._u._1;
    public ref readonly (int a, int b) D  => ref _impl._u._2;
    // E is a Tag only so there is no value getter
    public ref readonly List<int>? F  => ref Unsafe.As<object?, List<int>?>(ref _impl._c0);
    public ref readonly (int a, string b) G  => ref _impl._f0_0;
    // When gets a record variant, it will get a magic ref struct for forwarding field references
    public VariantH H => new(ref _impl);
    
    ... Eq Cmp ToString
    
    public readonly ref struct VariantH
    {
        private ref readonly __impl_ _impl;
        
        public ref readonly int a => ref _impl._u._3._0;
        public ref readonly int b  => ref _impl._u._3._1;
        public ref readonly string c => ref Unsafe.As<object?, string>(ref _impl._c0);
        public ref readonly HashSet<int> d  => ref Unsafe.As<object?, HashSet<int>>(ref _impl._c1);
        public ref readonly (int a, string b) e  => ref _impl._f0_0;
        
        ... Eq Cmp ToString
    }
}

Complete generate output:

<details> <summary>Union1.union.g.cs</summary>

// <auto-generated/>

#nullable disable warnings
#nullable enable annotations

using Coplt.Union;

public readonly partial struct Union1
    : global::Coplt.Union.ITaggedUnion
    , global::System.IEquatable<Union1>
    , global::System.IComparable<Union1>
    #if NET7_0_OR_GREATER
    , global::System.Numerics.IEqualityOperators<Union1, Union1, bool>
    , global::System.Numerics.IComparisonOperators<Union1, Union1, bool>
    #endif
{
    #region Fields

    private readonly __impl_ _impl;

    #endregion // Fields

    #region Ctor

    private Union1(__impl_ _impl) { this._impl = _impl; }

    #endregion // Ctor

    #region Tag Getter

    public readonly Tags Tag
    {
        [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
        get => this._impl._tag;
    }

    #endregion // Tag Getter

    #region Tags

    public enum Tags : byte
    {
        A = 1,
        B,
        C,
        D,
        E,
        F,
        G,
        H,
    }

    #endregion // Tags

    #region Impl

    [global::System.Runtime.CompilerServices.CompilerGenerated]
    [global::System.Runtime.InteropServices.StructLayout(global::System.Runtime.InteropServices.LayoutKind.Auto)]
    internal struct __impl_
    {
        public object? _c0;
        public object? _c1;
        public __unmanaged_ _u;
        public (int a, string b) _f0_0;
        public readonly Tags _tag;

        [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
        public __impl_(Tags _tag)
        {
            this._c0 = null;
            this._c1 = null;
            global::System.Runtime.CompilerServices.Unsafe.SkipInit(out this._u);
            this._f0_0 = default!;
            this._tag = _tag;
        }

        [global::System.Runtime.CompilerServices.CompilerGenerated]
        [global::System.Runtime.InteropServices.StructLayout(global::System.Runtime.InteropServices.LayoutKind.Explicit)]
        internal struct __unmanaged_
        {
            [global::System.Runtime.InteropServices.FieldOffset(0)]
            public int _0;
            [global::System.Runtime.InteropServices.FieldOffset(0)]
            public bool _1;
            [global::System.Runtime.InteropServices.FieldOffset(0)]
            public (int a, int b) _2;
            [global::System.Runtime.InteropServices.FieldOffset(0)]
            public __record_7_unmanaged_ _3;
        }

        [global::System.Runtime.CompilerServices.CompilerGenerated]
        [global::System.Runtime.InteropServices.StructLayout(global::System.Runtime.InteropServices.LayoutKind.Auto)]
        internal struct __record_7_unmanaged_
        {
            public int _0;
            public int _1;
        }
    }

    #endregion // Impl

    #region Views

    public readonly ref struct VariantH
        #if NET9_0_OR_GREATER
        : global::System.IEquatable<VariantH>
        , global::System.IComparable<VariantH>
        #endif
    {
        #region Fields

        private readonly ref readonly __impl_ _impl;

        #endregion // Fields

        #region Ctor

        [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
        internal VariantH(ref readonly __impl_ impl)
        {
            _impl = ref impl;
        }

        #endregion // Ctor

        #region Getter

        public ref readonly int a
        {
            [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
            get => ref global::System.Runtime.CompilerServices.Unsafe.AsRef<Union1.__impl_>(in this._impl)._u._3._0;
        }
        public ref readonly int b
        {
            [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
            get => ref global::System.Runtime.CompilerServices.Unsafe.AsRef<Union1.__impl_>(in this._impl)._u._3._1;
        }
        public ref readonly string c
        {
            [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
            get => ref global::System.Runtime.CompilerServices.Unsafe.As<object?, string>(ref global::System.Runtime.CompilerServices.Unsafe.AsRef<Union1.__impl_>(in this._impl)._c0);
        }
        public ref readonly global::System.Collections.Generic.HashSet<int> d
        {
            [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
            get => ref global::System.Runtime.CompilerServices.Unsafe.As<object?, global::System.Collections.Generic.HashSet<int>>(ref global::System.Runtime.CompilerServices.Unsafe.AsRef<Union1.__impl_>(in this._impl)._c1);
        }
        public ref readonly (int a, string b) e
        {
            [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
            get => ref global::System.Runtime.CompilerServices.Unsafe.AsRef<Union1.__impl_>(in this._impl)._f0_0;
        }

        #endregion // Getter

        #region Equals

        [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
        public bool Equals(scoped VariantH other) =>
            global::System.Collections.Generic.EqualityComparer<int>.Default.Equals(a, other.a) &&
            global::System.Collections.Generic.EqualityComparer<int>.Default.Equals(b, other.b) &&
            global::System.Collections.Generic.EqualityComparer<string>.Default.Equals(c, other.c) &&
            global::System.Collections.Generic.EqualityComparer<global::System.Collections.Generic.HashSet<int>>.Default.Equals(d, other.d) &&
            global::System.Collections.Generic.EqualityComparer<(int a, string b)>.Default.Equals(e, other.e);

        [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
        public override int GetHashCode() => global::System.HashCode.Combine(a, b, c, d, e);

        [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
        public override bool Equals(object? obj) => false;

        [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
        public static bool operator ==(scoped VariantH left, scoped VariantH right) => left.Equals(right);
        [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
        public static bool operator !=(scoped VariantH left, scoped VariantH right) => !left.Equals(right);

        #endregion // Equals

        #region CompareTo

        [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
        public int CompareTo(scoped VariantH other)
        {
            var _0 = global::System.Collections.Generic.Comparer<int>.Default.Compare(a, other.a);
            if (_0 != 0) return _0;
            var _1 = global::System.Collections.Generic.Comparer<int>.Default.Compare(b, other.b);
            if (_1 != 0) return _1;
            var _2 = global::System.Collections.Generic.Comparer<string>.Default.Compare(c, other.c);
            if (_2 != 0) return _2;
            var _3 = global::System.Collections.Generic.Comparer<global::System.Collections.Generic.HashSet<int>>.Default.Compare(d, other.d);
            if (_3 != 0) return _3;
            var _4 = global::System.Collections.Generic.Comparer<(int a, string b)>.Default.Compare(e, other.e);
            if (_4 != 0) return _4;
            return 0;
        }

        [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
        public static bool operator <(scoped VariantH left, scoped VariantH right) => left.CompareTo(right) < 0;
        [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
        public static bool operator >(scoped VariantH left, scoped VariantH right) => left.CompareTo(right) > 0;
        [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
        public static bool operator <=(scoped VariantH left, scoped VariantH right) => left.CompareTo(right) <= 0;
        [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
        public static bool operator >=(scoped VariantH left, scoped VariantH right) => left.CompareTo(right) >= 0;

        #endregion // CompareTo

        #region ToString

        [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
        public override string ToString() => $"{nameof(Union1)}.{nameof(Tags.H)} {{ a = {a}, b = {b}, c = {c}, d = {d}, e = {e} }}";

        #endregion // ToString

        #region Deconstruct

        [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
        public void Deconstruct(out int a, out int b, out string c, out global::System.Collections.Generic.HashSet<int> d, out (int a, string b) e)
        {
            a = this.a;
            b = this.b;
            c = this.c;
            d = this.d;
            e = this.e;
        }

        #endregion // Deconstruct
    }

    #endregion // Views

    #region Make

    [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
    public static Union1 MakeA(int value)
    {
        var _impl = new __impl_(Tags.A);
        _impl._u._0 = value;
        return new Union1(_impl);
    }

    [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
    public static Union1 MakeB(string value)
    {
        var _impl = new __impl_(Tags.B);
        _impl._c0 = value;
        return new Union1(_impl);
    }

    [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
    public static Union1 MakeC(bool value)
    {
        var _impl = new __impl_(Tags.C);
        _impl._u._1 = value;
        return new Union1(_impl);
    }

    [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
    public static Union1 MakeD((int a, int b) value)
    {
        var _impl = new __impl_(Tags.D);
        _impl._u._2 = value;
        return new Union1(_impl);
    }

    [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
    public static Union1 MakeE()
    {
        var _impl = new __impl_(Tags.E);
        return new Union1(_impl);
    }

    [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
    public static Union1 MakeF(global::System.Collections.Generic.List<int>? value)
    {
        var _impl = new __impl_(Tags.F);
        _impl._c0 = value;
        return new Union1(_impl);
    }

    [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
    public static Union1 MakeG((int a, string b) value)
    {
        var _impl = new __impl_(Tags.G);
        _impl._f0_0 = value;
        return new Union1(_impl);
    }

    [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
    public static Union1 MakeH(int a, int b, string c, global::System.Collections.Generic.HashSet<int> d, (int a, string b) e)
    {
        var _impl = new __impl_(Tags.H);
        _impl._u._3._0 = a;
        _impl._u._3._1 = b;
        _impl._c0 = c;
        _impl._c1 = d;
        _impl._f0_0 = e;
        return new Union1(_impl);
    }

    #endregion // Make

    #region Is

    public readonly bool IsA
    {
        [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
        get => this._impl._tag == Tags.A;
    }

    public readonly bool IsB
    {
        [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
        get => this._impl._tag == Tags.B;
    }

    public readonly bool IsC
    {
        [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
        get => this._impl._tag == Tags.C;
    }

    public readonly bool IsD
    {
        [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
        get => this._impl._tag == Tags.D;
    }

    public readonly bool IsE
    {
        [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
        get => this._impl._tag == Tags.E;
    }

    public readonly bool IsF
    {
        [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
        get => this._impl._tag == Tags.F;
    }

    public readonly bool IsG
    {
        [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
        get => this._impl._tag == Tags.G;
    }

    public readonly bool IsH
    {
        [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
        get => this._impl._tag == Tags.H;
    }

    #endregion // Is

    #region Getter

    [global::System.Diagnostics.CodeAnalysis.UnscopedRef]
    public ref readonly int A
    {
        [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
        get => ref !this.IsA ? ref global::System.Runtime.CompilerServices.Unsafe.NullRef<int>() : ref this._impl._u._0!;
    }

    [global::System.Diagnostics.CodeAnalysis.UnscopedRef]
    public ref readonly string B
    {
        [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
        get => ref !this.IsB ? ref global::System.Runtime.CompilerServices.Unsafe.NullRef<string>() : ref global::System.Runtime.CompilerServices.Unsafe.As<object?, string>(ref global::System.Runtime.CompilerServices.Unsafe.AsRef<Union1.__impl_>(in this._impl)._c0);
    }

    [global::System.Diagnostics.CodeAnalysis.UnscopedRef]
    public ref readonly bool C
    {
        [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
        get => ref !this.IsC ? ref global::System.Runtime.CompilerServices.Unsafe.NullRef<bool>() : ref this._impl._u._1!;
    }

    [global::System.Diagnostics.CodeAnalysis.UnscopedRef]
    public ref readonly (int a, int b) D
    {
        [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
        get => ref !this.IsD ? ref global::System.Runtime.CompilerServices.Unsafe.NullRef<(int a, int b)>() : ref this._impl._u._2!;
    }

    [global::System.Diagnostics.CodeAnalysis.UnscopedRef]
    public ref readonly global::System.Collections.Generic.List<int>? F
    {
        [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
        get => ref !this.IsF ? ref global::System.Runtime.CompilerServices.Unsafe.NullRef<global::System.Collections.Generic.List<int>?>() : ref global::System.Runtime.CompilerServices.Unsafe.As<object?, global::System.Collections.Generic.List<int>?>(ref global::System.Runtime.CompilerServices.Unsafe.AsRef<Union1.__impl_>(in this._impl)._c0);
    }

    [global::System.Diagnostics.CodeAnalysis.UnscopedRef]
    public ref readonly (int a, string b) G
    {
        [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
        get => ref !this.IsG ? ref global::System.Runtime.CompilerServices.Unsafe.NullRef<(int a, string b)>() : ref this._impl._f0_0!;
    }

    [global::System.Diagnostics.CodeAnalysis.UnscopedRef]
    public VariantH H
    {
        [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
        get => !this.IsH ? throw new global::System.NullReferenceException() : new VariantH(ref global::System.Runtime.CompilerServices.Unsafe.AsRef<Union1.__impl_>(in this._impl));
    }

    #endregion // Getter

    #region Equals

    [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
    public readonly bool Equals(Union1 other) => this.Tag != other.Tag ? false : this.Tag switch
    {
        Tags.A => global::System.Collections.Generic.EqualityComparer<int>.Default.Equals(this.A, other.A),
        Tags.B => global::System.Collections.Generic.EqualityComparer<string>.Default.Equals(this.B, other.B),
        Tags.C => global::System.Collections.Generic.EqualityComparer<bool>.Default.Equals(this.C, other.C),
        Tags.D => global::System.Collections.Generic.EqualityComparer<(int a, int b)>.Default.Equals(this.D, other.D),
        Tags.F => global::System.Collections.Generic.EqualityComparer<global::System.Collections.Generic.List<int>?>.Default.Equals(this.F, other.F),
        Tags.G => global::System.Collections.Generic.EqualityComparer<(int a, string b)>.Default.Equals(this.G, other.G),
        Tags.H => this.H.Equals(other.H),
        _ => true,
    };

    [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
    public readonly override int GetHashCode() => this.Tag switch
    {
        Tags.A => global::System.HashCode.Combine(this.Tag, this.A),
        Tags.B => global::System.HashCode.Combine(this.Tag, this.B),
        Tags.C => global::System.HashCode.Combine(this.Tag, this.C),
        Tags.D => global::System.HashCode.Combine(this.Tag, this.D),
        Tags.F => global::System.HashCode.Combine(this.Tag, this.F),
        Tags.G => global::System.HashCode.Combine(this.Tag, this.G),
        Tags.H => global::System.HashCode.Combine(this.Tag, this.H.GetHashCode()),
        _ => global::System.HashCode.Combine(this.Tag),
    };

    [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
    public readonly override bool Equals(object? obj) => obj is Union1 other && Equals(other);

    [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
    public static bool operator ==(Union1 left, Union1 right) => left.Equals(right);
    [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
    public static bool operator !=(Union1 left, Union1 right) => !left.Equals(right);

    #endregion // Equals

    #region CompareTo

    [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
    public readonly int CompareTo(Union1 other) => this.Tag != other.Tag ? global::System.Collections.Generic.Comparer<Tags>.Default.Compare(this.Tag, other.Tag) : this.Tag switch
    {
        Tags.A => global::System.Collections.Generic.Comparer<int>.Default.Compare(this.A, other.A),
        Tags.B => global::System.Collections.Generic.Comparer<string>.Default.Compare(this.B, other.B),
        Tags.C => global::System.Collections.Generic.Comparer<bool>.Default.Compare(this.C, other.C),
        Tags.D => global::System.Collections.Generic.Comparer<(int a, int b)>.Default.Compare(this.D, other.D),
        Tags.F => global::System.Collections.Generic.Comparer<global::System.Collections.Generic.List<int>?>.Default.Compare(this.F, other.F),
        Tags.G => global::System.Collections.Generic.Comparer<(int a, string b)>.Default.Compare(this.G, other.G),
        Tags.H => this.H.CompareTo(other.H),
        _ => 0,
    };

    [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
    public static bool operator <(Union1 left, Union1 right) => left.CompareTo(right) < 0;
    [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
    public static bool operator >(Union1 left, Union1 right) => left.CompareTo(right) > 0;
    [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
    public static bool operator <=(Union1 left, Union1 right) => left.CompareTo(right) <= 0;
    [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
    public static bool operator >=(Union1 left, Union1 right) => left.CompareTo(right) >= 0;

    #endregion // CompareTo

    #region ToString

    [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
    public readonly override string ToString() => this.Tag switch
    {
        Tags.A => $"{nameof(Union1)}.{nameof(Tags.A)} {{ {(this.A)} }}",
        Tags.B => $"{nameof(Union1)}.{nameof(Tags.B)} {{ {(this.B)} }}",
        Tags.C => $"{nameof(Union1)}.{nameof(Tags.C)} {{ {(this.C)} }}",
        Tags.D => $"{nameof(Union1)}.{nameof(Tags.D)} {{ {(this.D)} }}",
        Tags.E => $"{nameof(Union1)}.{nameof(Tags.E)}",
        Tags.F => $"{nameof(Union1)}.{nameof(Tags.F)} {{ {(this.F)} }}",
        Tags.G => $"{nameof(Union1)}.{nameof(Tags.G)} {{ {(this.G)} }}",
        Tags.H => $"{(this.H.ToString())}",
        _ => nameof(Union1),
    };

    #endregion // ToString
}

</details>

How to use

You can manually determine the Tag or use pattern matching.
But remember C# does not have enum exhaustion semantics.

var u = Union1.MakeA(123);

if (u is { Tag: Union1.Tags.A, A: var a }) { }

if (u is { IsA: true, A: var a }) { }

if (u.IsA)
{
    var a = u.A;
}

switch (u.Tag)
{
    case Union1.Tags.A:
        break;
  ...
}

switch (u.Tag)
{
    case { IsA: true, A: var a }:
        break;
  ...
}

Examples

Foo

// sizeof(Foo) == 16 (8 data, 1 tag, 7 padding)
[Union]
public partial struct Foo
{
    [UnionTemplate]
    private interface Template
    {
        int A(); 
        float B();
        long C();
        double D();
    }
}

Option

[Union]
public partial struct Option<T>
{
    [UnionTemplate]
    private interface Template
    {
        void None();    // tag is 0
        T Some();
    }
}

Result

[Union]
public partial struct Result<T, E>
{
    [UnionTemplate]
    private interface Template
    {
        T Ok();    // tag is 1
        E Err();
    }
}

Shape

F# Shape

[Union]
public partial struct Shape
{
    [UnionTemplate]
    private interface Template
    {
        void Rectangle(float Width, float Length);
        void Circle(float Radius);
        void Prism(float Width1, float Width2, float Height);
    }
}

public static void Foo()
{
    var rect = Shape.MakeRectangle(Length: 1.3f, Width: 1.3f);
    var circ = Shape.MakeCircle(1.0f);
    var prism = Shape.MakePrism(5f, 2f, Height: 3f);
}
public static void Foo(Shape shape)
{
    switch (shape)
    {
        case { IsRectangle: true, Rectangle.Width: var width }:
            break;
        case { IsCircle: true, Circle.Radius: var radius }:
            break;
        case { IsPrism: true, Prism.Width1: var width }:
            break;
    }
}

Tree

F# Tree

[Union]
public partial class Tree
{
    [UnionTemplate]
    private interface Template
    {
        void Tip();
        void Node(int Value, Tree Left, Tree Right);
    }

    public static int Sum(Tree Tree) => Tree switch
    {
        // Node: is a ref struct, which will avoid copying when matching patterns
        { IsNode: true, Node: var (value, left, right) } => value + Sum(left) + Sum(right),
        _ => 0,
    };

    public static int Sum2(Tree Tree) => Tree switch
    {
        // Node: is a ref struct, which will avoid copying when matching patterns
        { IsNode: true, Node: var node } =>
            node.Value + Sum(node.Left) + Sum(node.Right),
        _ => 0,
    };
}
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
0.16.0 117 3/16/2025
0.15.0 160 3/11/2025
0.14.0 149 3/11/2025
0.13.2 169 3/9/2025
0.13.1 111 3/9/2025
0.13.0 113 3/9/2025
0.12.1 167 3/8/2025
0.12.0 207 3/5/2025
0.11.0 95 2/25/2025
0.10.0 90 2/23/2025
0.9.0 88 2/23/2025