AltaSoft.Choice 1.3.1

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

AltaSoft.Choice

NuGet - AltaSoft.Choice NuGet - AltaSoft.Choice.Generator Dot NET 8+

AltaSoft.ChoiceGenerator is a lightweight C# source generator that allows you to define choice types (discriminated unions) with minimal syntax.


✨ Features

  • Simple [Choice] attribute for defining alternatives
  • Generates type-safe properties
  • Supports XML and System.Text.Json serialization
  • Includes CreateAsXxx, Match, and Switch methods
  • Auto-generates enum for valid choice types
  • Implicit conversion operators for easy usage
  • Generates XmlSerializer

🛠️ Installation

Add the following NuGet packages to your project:

<ItemGroup>
  <PackageReference Include="AltaSoft.Choice" Version="x.x.x" />
  <PackageReference Include="AltaSoft.Choice.Generator" Version="x.x.x" PrivateAssets="all" />
</ItemGroup>

✅ Define Your Choice Type

Mark your class with [Choice] and define partial nullable properties :

using AltaSoft.Choice;
namespace AltaSoft.ChoiceGenerator.Tests;

[Choice]
public sealed partial class Authorisation1Choice
{
    /// <summary>
    /// <para>Specifies the authorisation, in a coded form.</para>
    /// </summary>
    [XmlTag("Cd")]
    [JsonPropertyName("cd")]
    public partial Authorisation1Code? Code { get; set; }

    /// <summary>
    /// <para>Specifies the authorisation, in a free text form.</para>
    /// </summary>
    [XmlTag("Prtry")]
    public partial Proprietary? Proprietary { get; set; }
}

⚙️ Generated Code

Below is the generated code for the example above:

//------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated by 'AltaSoft Choice.Generator'.
//     Changes to this file may cause incorrect behavior and will be lost if the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------

#nullable enable

using AltaSoft.ChoiceGenerator.Tests;
using AltaSoft.Choice;
using System;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Xml;
using System.Xml.Serialization;
using System.Xml.Schema;

namespace AltaSoft.ChoiceGenerator.Tests;

#pragma warning disable CS8774 // Member must have a non-null value when exiting.
#pragma warning disable CS0628 // New protected member declared in sealed type

public sealed partial record Authorisation1Choice
{
    [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
    public Authorisation1Choice()
    {
    }

    /// <summary>
    /// <para>Choice enum </para>
    /// </summary>
    [JsonIgnore]
    [XmlIgnore]
    public ChoiceOf ChoiceType { get; private set; }

    private AltaSoft.ChoiceGenerator.Tests.Authorisation1Code? _code;

    /// <summary>
    /// Specifies the authorisation, in a coded form.
    /// </summary>
    [DisallowNull]
    [XmlElement("Cd")]
    public partial AltaSoft.ChoiceGenerator.Tests.Authorisation1Code? Code
    {
        get => _code;
        set
        {
            _code = value ?? throw new InvalidOperationException("Choice value cannot be null");
            _proprietary = null;
            ChoiceType = ChoiceOf.Code;
        }
    }

    private Proprietary? _proprietary;

    /// <summary>
    /// Specifies the authorisation, in a free text form.
    /// </summary>
    [DisallowNull]
    [XmlElement("Prtry")]
    public partial Proprietary? Proprietary
    {
        get => _proprietary;
        set
        {
            _proprietary = value ?? throw new InvalidOperationException("Choice value cannot be null");
            _code = null;
            ChoiceType = ChoiceOf.Proprietary;
        }
    }


    /// <summary>
    /// Creates a new <see cref="AltaSoft.ChoiceGenerator.Tests.Authorisation1Choice"/> instance and sets its value using the specified <see cref="AltaSoft.ChoiceGenerator.Tests.Authorisation1Code"/>.
    /// </summary>
    /// <param name="value">The value to assign to the created choice instance.</param>
    public static AltaSoft.ChoiceGenerator.Tests.Authorisation1Choice CreateAsCode(AltaSoft.ChoiceGenerator.Tests.Authorisation1Code value) => new () { Code = value };

    /// <summary>
    /// Creates a new <see cref="AltaSoft.ChoiceGenerator.Tests.Authorisation1Choice"/> instance and sets its value using the specified <see cref="Proprietary"/>.
    /// </summary>
    /// <param name="value">The value to assign to the created choice instance.</param>
    public static AltaSoft.ChoiceGenerator.Tests.Authorisation1Choice CreateAsProprietary(Proprietary value) => new () { Proprietary = value };

    /// <summary>
    /// <para>Applies the appropriate function based on the current choice type</para>
    /// </summary>
    /// <typeparam name="TResult">The return type of the provided match functions</typeparam>
    /// <param name="matchCode">Function to invoke if the choice is a <see cref="ChoiceOf.Code"/> value</param>
    /// <param name="matchProprietary">Function to invoke if the choice is a <see cref="ChoiceOf.Proprietary"/> value</param>
    public TResult Match<TResult>(
    	Func<AltaSoft.ChoiceGenerator.Tests.Authorisation1Code, TResult> matchCode, 
    	Func<Proprietary, TResult> matchProprietary)
    {
        return ChoiceType switch
        {
            ChoiceOf.Code => matchCode(Code!.Value),
            ChoiceOf.Proprietary => matchProprietary(Proprietary!),
            _ => throw new InvalidOperationException($"Invalid ChoiceType. '{ChoiceType}'")
        };
    }

    /// <summary>
    /// <para>Applies the appropriate Action based on the current choice type</para>
    /// </summary>
    /// <param name="matchCode">Action to invoke if the choice is a <see cref="ChoiceOf.Code"/> value</param>
    /// <param name="matchProprietary">Action to invoke if the choice is a <see cref="ChoiceOf.Proprietary"/> value</param>
    public void Switch(
    	Action<AltaSoft.ChoiceGenerator.Tests.Authorisation1Code> matchCode, 
    	Action<Proprietary> matchProprietary)
    {
        switch (ChoiceType)
        {
            case ChoiceOf.Code:
                matchCode(Code!.Value);
                return;

            case ChoiceOf.Proprietary:
                matchProprietary(Proprietary!);
                return;

            default:
            throw new XmlException($"Invalid ChoiceType. '{ChoiceType}'");
        }
    }

    /// <summary>
    /// Implicitly converts an <see cref="AltaSoft.ChoiceGenerator.Tests.Authorisation1Code"/> to an <see cref="Authorisation1Choice"/>.
    /// </summary>
    /// <param name="value">The <see cref="AltaSoft.ChoiceGenerator.Tests.Authorisation1Code"/> to convert.</param>
    /// <returns>
    /// <see cref="Authorisation1Choice"/> instance representing the code.
    /// </returns>
    public static implicit operator Authorisation1Choice(AltaSoft.ChoiceGenerator.Tests.Authorisation1Code value) => CreateAsCode(value);

    /// <summary>
    /// Implicitly converts an <see cref="Proprietary"/> to an <see cref="Authorisation1Choice"/>.
    /// </summary>
    /// <param name="value">The <see cref="Proprietary"/> to convert.</param>
    /// <returns>
    /// <see cref="Authorisation1Choice"/> instance representing the code.
    /// </returns>
    public static implicit operator Authorisation1Choice(Proprietary value) => CreateAsProprietary(value);

    /// <summary>
    /// Determines whether the <see cref="Code"/> property should be serialized.
    /// </summary>
    /// <returns>
    /// <c>true</c> if <see cref="Code"/> has a value; otherwise, <c>false</c>.
    /// </returns>
    [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
    public bool ShouldSerializeCode() => Code.HasValue;

    /// <summary>
    /// <para>Choice enumeration</para>
    /// </summary>
    [XmlType("ChoiceOf.Authorisation1Choice")]
    public enum ChoiceOf
    {
        /// <summary>
        /// Specifies the authorisation, in a coded form.
        /// </summary>
        Code, 
        /// <summary>
        /// Specifies the authorisation, in a free text form.
        /// </summary>
        Proprietary, 
    }
}


💡 Example Usage

Creating with CreateAs methods

var choice = Authorisation1Choice.CreateAsCode(Authorisation1Code.FileLevelAuthorisationSummary);

var result = choice.Match(
    code => $"It's a code: {code}",
    prop => $"It's proprietary: {prop.ToString()}"
);

choice.Switch(
    code => Console.WriteLine($"String: {code}"),
    prop => Console.WriteLine($"Number: {prop.ToString()}")
);

Creating using implicit operators

if property types are distinct implicit operators are generated

Authosiration1Choice choice = Authorisation1Code.FileLevelAuthorisationSummary;

📦 Projects

  • AltaSoft.Choice
    Contains the [Choice] marker attribute

  • AltaSoft.Choice.Generator
    Implements the source generator that produces boilerplate code


📄 License

This project is licensed under the MIT License.

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  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 is compatible.  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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net8.0

    • No dependencies.
  • net9.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.3.1 227 5/16/2025
1.3.0 186 5/16/2025
1.2.3 219 5/15/2025
1.2.2 231 5/14/2025
1.2.1 145 5/8/2025