Dunet 1.7.0
See the version list below for details.
dotnet add package Dunet --version 1.7.0
NuGet\Install-Package Dunet -Version 1.7.0
<PackageReference Include="Dunet" Version="1.7.0"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> </PackageReference>
paket add Dunet --version 1.7.0
#r "nuget: Dunet, 1.7.0"
// Install Dunet as a Cake Addin #addin nuget:?package=Dunet&version=1.7.0 // Install Dunet as a Cake Tool #tool nuget:?package=Dunet&version=1.7.0
Dunet
Dunet is a simple source generator for discriminated unions in C#.
Install
- NuGet:
dotnet add package dunet
Usage
// 1. Import the namespace.
using Dunet;
// 2. Add the `Union` attribute to a partial record.
[Union]
partial record Shape
{
// 3. Define the union members as inner partial records.
partial record Circle(double Radius);
partial record Rectangle(double Length, double Width);
partial record Triangle(double Base, double Height);
}
// 4. Use the union members.
var shape = new Shape.Rectangle(3, 4);
var area = shape.Match(
circle => 3.14 * circle.Radius * circle.Radius,
rectangle => rectangle.Length * rectangle.Width,
triangle => triangle.Base * triangle.Height / 2
);
Console.WriteLine(area); // "12"
Generics Support
Use generics for more advanced union types. For example, an option monad:
// 1. Import the namespace.
using Dunet;
// Optional: use static import for more terse code.
using static Option<int>;
// 2. Add the `Union` attribute to a partial record.
// 3. Add one or more type arguments to the union record.
[Union]
partial record Option<T>
{
partial record Some(T Value);
partial record None();
}
// 4. Use the union members.
Option<int> ParseInt(string? value) =>
int.TryParse(value, out var number)
? new Some(number)
: new None();
string GetOutput(Option<int> number) =>
number.Match(
some => some.Value.ToString(),
none => "Invalid input!"
);
var input = Console.ReadLine(); // User inputs "not a number".
var result = ParseInt(input);
var output = GetOutput(result);
Console.WriteLine(output); // "Invalid input!"
input = Console.ReadLine(); // User inputs "12345".
result = ParseInt(input);
output = GetOutput(result);
Console.WriteLine(output); // "12345".
Implicit Conversion Support
Dunet generates implicit conversions between union member values and the union
type if your union meets all of the following conditions:
- All members contain only a single parameter.
- All parameters are a different type.
- No parameters are an interface type.
For example, consider a Result
union type that represents success
as a double
and failure as an Exception
:
// 1. Import the namespace.
using Dunet;
// 2. Define a union type with single unique member values:
[Union]
partial record Result
{
partial record Success(double Value);
partial record Failure(Exception Error);
}
// 3. Return union member values directly.
Result Divide(double numerator, double denominator)
{
if (denominator is 0d)
{
// No need for `new Result.Failure(new InvalidOperationException("..."));`
return new InvalidOperationException("Cannot divide by zero!");
}
// No need for `new Result.Success(...);`
return numerator / denominator;
}
var result = Divide(42, 0);
var output = result.Match(
success => success.Value.ToString(),
failure => failure.Error.Message
);
Console.WriteLine(output); // "Cannot divide by zero!"
Async Match Support
Dunet generates a MatchAsync()
extension method for all Task<T>
and
ValueTask<T>
where T
is a union type. For example:
// Choice.cs
using Dunet;
namespace Core;
// 1. Define a union type within a namespace.
[Union]
partial record Choice
{
partial record Yes;
partial record No(string Reason);
}
// Program.cs
using Core;
using static Core.Choice;
// 2. Define async methods like you would for any other type.
static async Task<Choice> AskAsync()
{
// Simulating network call.
await Task.Delay(1000);
// 3. Return unions from async methods like any other type.
return new No("because I don't wanna!");
}
// 4. Asynchronously match any union `Task` or `ValueTask`.
var response = await AskAsync()
.MatchAsync(
yes => "Yes!!!",
no => $"No, {no.Reason}"
);
// Prints "No, because I don't wanna!" after 1 second.
Console.WriteLine(response);
Note:
MatchAsync()
can only be generated for namespaced unions.
Match on Specific Union Member
Dunet generates specific match methods for each union member. This is useful
when unwrapping a union and you only care about transforming a single variant.
For example:
[Union]
partial record Shape
{
partial record Point(int X, int Y);
partial record Line(double Length);
partial record Rectangle(double Length, double Width);
partial record Sphere(double Radius);
}
public static bool IsZeroDimensional(this Shape shape) =>
shape.MatchPoint(
point => true,
() => false
);
public static bool IsOneDimensional(this Shape shape) =>
shape.MatchLine(
line => true,
() => false
);
public static bool IsTwoDimensional(this Shape shape) =>
shape.MatchRectangle(
rectangle => true,
() => false
);
public static bool IsThreeDimensional(this Shape shape) =>
shape.MatchSphere(
sphere => true,
() => false
);
Nested Union Support
To declare a union nested within a class or record, the class or record must
be partial
. For example:
// This type declaration must be partial.
public partial class Parent1
{
// So must this one.
public partial class Parent2
{
// Unions must always be partial.
[Union]
public partial record Nested
{
public partial record Member1;
public partial record Member2;
}
}
}
// Access union members like any other nested type.
var member1 = new Parent1.Parent2.Nested.Member1();
Samples
Learn more about Target Frameworks and .NET Standard.
-
.NETStandard 2.0
- Microsoft.CodeAnalysis.CSharp (>= 4.4.0)
NuGet packages (2)
Showing the top 2 NuGet packages that depend on Dunet:
Package | Downloads |
---|---|
ZeroC.Slice
Slice for C#. |
|
Xenial.Identity.Client
Package Description |
GitHub repositories (2)
Showing the top 2 popular GitHub repositories that depend on Dunet:
Repository | Stars |
---|---|
domn1995/dunet
C# discriminated union source generator
|
|
icerpc/icerpc-csharp
A C# RPC framework built for QUIC, with bidirectional streaming, first-class async/await, and Protobuf support.
|
Version | Downloads | Last updated | |
---|---|---|---|
1.11.2 | 52,831 | 1/29/2024 | |
1.11.1 | 315 | 1/27/2024 | |
1.11.0 | 8,753 | 1/4/2024 | |
1.11.0-alpha1 | 349 | 1/4/2024 | |
1.10.0 | 10,846 | 11/2/2023 | |
1.9.0 | 4,483 | 9/13/2023 | |
1.8.0 | 14,126 | 5/16/2023 | |
1.7.2-pre1 | 1,430 | 5/12/2023 | |
1.7.1 | 7,686 | 2/17/2023 | |
1.7.0 | 1,473 | 1/23/2023 | |
1.6.0 | 3,081 | 1/3/2023 | |
1.6.0-preview1 | 1,177 | 1/3/2023 | |
1.5.0 | 11,342 | 11/13/2022 | |
1.4.2 | 73,595 | 11/5/2022 | |
1.4.1 | 1,504 | 11/5/2022 | |
1.4.1-preview2 | 1,409 | 11/5/2022 | |
1.4.1-preview1 | 1,393 | 11/5/2022 | |
1.4.0 | 2,025 | 10/6/2022 | |
1.3.0 | 1,504 | 9/22/2022 | |
1.2.0 | 1,806 | 8/27/2022 | |
1.1.0 | 3,262 | 7/11/2022 | |
1.0.2 | 1,678 | 7/8/2022 | |
1.0.1 | 1,650 | 7/6/2022 | |
1.0.0 | 1,063 | 6/27/2022 | |
0.5.0 | 908 | 6/26/2022 | |
0.4.0 | 908 | 6/26/2022 | |
0.3.0 | 915 | 6/24/2022 | |
0.2.2 | 889 | 6/18/2022 | |
0.2.1 | 926 | 6/16/2022 | |
0.2.0 | 927 | 6/16/2022 | |
0.1.3 | 936 | 6/15/2022 | |
0.1.2 | 909 | 6/13/2022 | |
0.1.1 | 939 | 6/13/2022 | |
0.1.0 | 996 | 6/13/2022 |
1.7.0 - Match on specific union member.
1.6.0 - Support generator cancellation.
1.5.0 - Support async Action match functions.
1.4.2 - Disables implicit conversions when union member is an interface type.
1.4.1 - Disable CS1591 in generated code.
1.4.0 - Support Action match functions.
1.3.0 - Async match methods.
1.2.0 - Support nested union declarations.
1.1.0 - Add implicit conversions for union members.
1.0.2 - Support multiple unions with the same name.
1.0.1 - Configure Dunet as development dependency.
1.0.0 - Production release.
0.5.0 - Generate dedicated match method on union records.
0.4.0 - Add support for declaring a union with records.
0.3.0 - Support complex types in union members.
0.2.2 - Add source generator DLL to package.
0.2.1 - Update readme.
0.2.0 - Generate dedicated match method.
0.1.3 - Add support for declaring unions inside top level programs.
0.1.2 - Simplify generated source.
0.1.1 - Support any number of interfaces.
0.1.0 - Initial release.