VariantEnum 1.0.1
dotnet add package VariantEnum --version 1.0.1
NuGet\Install-Package VariantEnum -Version 1.0.1
<PackageReference Include="VariantEnum" Version="1.0.1"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> </PackageReference>
paket add VariantEnum --version 1.0.1
#r "nuget: VariantEnum, 1.0.1"
// Install VariantEnum as a Cake Addin #addin nuget:?package=VariantEnum&version=1.0.1 // Install VariantEnum as a Cake Tool #tool nuget:?package=VariantEnum&version=1.0.1
VariantEnum
VariantEnum
is C# Source Generator that automatically creates a Rust Enum-like record class
from an Enum
, where each variant can have a value.
The automatically created record
class makes use of .NET 8 and C# 12 language features (Incremental Generator
, ISpanParsable<T>
, static abstract interfaces and Collection expressions).
This library is distributed via NuGet.
PM> Install-Package VariantEnum
How to use
If the Enum
definition name ends with 'Variant', a record class is automatically created with the same name as the enum definition name without 'Variant'.
Enum members can be assigned the [TVariantValueType]
attribute so that they have their own specific data.. Values can be accessed as argsXXX.
For example, if an IpAddrVariant
Enum is defined:
public enum IpAddrVariant : byte
{
[VariantValueType(typeof(byte), typeof(byte), typeof(byte), typeof(byte))]
V4,
[VariantValueType(typeof(string))]
V6,
None
}
This creates IpAddr
record class.
var ip = new IpAddr.V4(127, 0, 0, 1);
var value = (IpAddr)ip switch
{
IpAddr.V4 v4 => $"{v4.args0}.{v4.args1}.{v4.args2}.{v4.args3}",
IpAddr.V6 v6 => v6.args0,
_ => throw new Exception(),
};
Console.WriteLine(value); // 127.0.0.1
IpAddr
record class automatically generates the following APIs.
public abstract record IpAddr : ISpanFormattable, ISpanParsable<IpAddr>
{
public sealed record V4(byte args0, byte args1, byte args2, byte args3) : IpAddr
{
public static V4 Default => new V4(args0: default, args1: default, args2: default, args3: default);
public override bool TryFormat(Span<char> destination, out int charsWritten, ReadOnlySpan<char> format = default, IFormatProvider? provider = null);
}
public sealed record V6(string args0) : IpAddr
{
public static V6 Default => new V6(args0: default);
public override bool TryFormat(Span<char> destination, out int charsWritten, ReadOnlySpan<char> format = default, IFormatProvider? provider = null);
}
public sealed record None : IpAddr
{
public static None Default => new None();
public override bool TryFormat(Span<char> destination, out int charsWritten, ReadOnlySpan<char> format = default, IFormatProvider? provider = null);
}
// ISpanFormattable
public abstract bool TryFormat(Span<char> destination, out int charsWritten, ReadOnlySpan<char> format = default, IFormatProvider? provider = null);
public string ToString(string? format, IFormatProvider? formatProvider) => ToString();
// ISpanParsable<T>
public static IpAddr Parse(ReadOnlySpan<char> s, IFormatProvider? provider = default);
public static bool TryParse(ReadOnlySpan<char> s, IFormatProvider? provider, [MaybeNullWhen(false)] out IpAddr result);
// IParsable<T>
public static IpAddr Parse(string s, IFormatProvider? provider = default);
public static bool TryParse([NotNullWhen(true)] string? s, IFormatProvider? provider, [MaybeNullWhen(false)] out IpAddr result);
public static int Count { get; } => 3;
public static string? GetName(IpAddr t);
public static string[] GetNames() => [nameof(V4), nameof(V6), nameof(None)];
public static byte GetNumericValue(IpAddr t);
public static IpAddrVariant ConvertEnum(IpAddr t);
public static bool TryConvertEnum([NotNullWhen(true)] IpAddr? t, [MaybeNullWhen(false)] out IpAddrVariant result);
public static IpAddr Parse(ReadOnlySpan<char> s, bool ignoreCase, IFormatProvider? provider = default);
public static bool TryParse(ReadOnlySpan<char> s, out IpAddr result);
public static bool TryParse(ReadOnlySpan<char> s, bool ignoreCase, IFormatProvider? provider, [MaybeNullWhen(false)] out IpAddr result);
public static bool IsDefined(ReadOnlySpan<char> s);
public static bool IsDefined([NotNullWhen(true)] IpAddr? value);
}
<details><summary>Generated All Code(IpAddr.g.cs)</summary>
// <auto-generated> This .cs file is generated by VariantEnum. </auto-generated>
#nullable enable
#pragma warning disable CS0219 // The variable 'variable' is assigned but its value is never used
#pragma warning disable CS8600 // Converting null literal or possible null value to non-nullable type.
#pragma warning disable CS8601 // Possible null reference assignment.
#pragma warning disable CS8602 // Dereference of a possibly null reference.
#pragma warning disable CS8603 // Possible null reference return.
#pragma warning disable CS8604 // Possible null reference argument for parameter.
#pragma warning disable CS8619 // Possible null reference assignment fix
using System;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
namespace ConsoleApp;
public abstract record IpAddr :
ISpanFormattable,
ISpanParsable<IpAddr>
{
public sealed record V4(byte args0, byte args1, byte args2, byte args3) : IpAddr
{
public static V4 Default => new V4(args0: default, args1: default, args2: default, args3: default);
public override bool TryFormat(Span<char> destination, out int charsWritten, ReadOnlySpan<char> format = default, IFormatProvider? provider = null)
{
var index = 0;
charsWritten = 0;
if (destination.Length < 5)
{
charsWritten += index;
return false;
}
destination[index++] = 'V';
destination[index++] = '4';
destination[index++] = ' ';
destination[index++] = '{';
destination[index++] = ' ';
var handler = new DefaultInterpolatedStringHandler();
handler.AppendLiteral("args0 = ");
handler.AppendFormatted(args0);
handler.AppendFormatted(", " );
handler.AppendLiteral("args1 = ");
handler.AppendFormatted(args1);
handler.AppendFormatted(", " );
handler.AppendLiteral("args2 = ");
handler.AppendFormatted(args2);
handler.AppendFormatted(", " );
handler.AppendLiteral("args3 = ");
handler.AppendFormatted(args3);
var print = handler.ToStringAndClear();
var printSpan = print.AsSpan();
if (destination.Length < printSpan.Length + index)
{
charsWritten += index;
return false;
}
printSpan.CopyTo(destination.Slice(index, printSpan.Length));
index += printSpan.Length;
if (destination.Length < 2 + index)
{
charsWritten += index;
return false;
}
destination[index++] = ' ';
destination[index++] = '}';
charsWritten = index;
return true;
}
}
public sealed record V6(string args0) : IpAddr
{
public static V6 Default => new V6(args0: default);
public override bool TryFormat(Span<char> destination, out int charsWritten, ReadOnlySpan<char> format = default, IFormatProvider? provider = null)
{
var index = 0;
charsWritten = 0;
if (destination.Length < 5)
{
charsWritten += index;
return false;
}
destination[index++] = 'V';
destination[index++] = '6';
destination[index++] = ' ';
destination[index++] = '{';
destination[index++] = ' ';
var handler = new DefaultInterpolatedStringHandler();
handler.AppendLiteral("args0 = ");
handler.AppendFormatted(args0);
var print = handler.ToStringAndClear();
var printSpan = print.AsSpan();
if (destination.Length < printSpan.Length + index)
{
charsWritten += index;
return false;
}
printSpan.CopyTo(destination.Slice(index, printSpan.Length));
index += printSpan.Length;
if (destination.Length < 2 + index)
{
charsWritten += index;
return false;
}
destination[index++] = ' ';
destination[index++] = '}';
charsWritten = index;
return true;
}
}
public sealed record None : IpAddr
{
public static None Default => new None();
public override bool TryFormat(Span<char> destination, out int charsWritten, ReadOnlySpan<char> format = default, IFormatProvider? provider = null)
{
var index = 0;
charsWritten = 0;
if (destination.Length < 8)
{
charsWritten += index;
return false;
}
destination[index++] = 'N';
destination[index++] = 'o';
destination[index++] = 'n';
destination[index++] = 'e';
destination[index++] = ' ';
destination[index++] = '{';
destination[index++] = ' ';
destination[index++] = '}';
charsWritten = index;
return true;
}
}
public abstract bool TryFormat(Span<char> destination, out int charsWritten, ReadOnlySpan<char> format = default, IFormatProvider? provider = null);
public string ToString(string? format, IFormatProvider? formatProvider) => ToString();
public static int Count => 3;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static string? GetName(IpAddr ipaddr)
{
return ipaddr switch
{
V4 => nameof(V4),
V6 => nameof(V6),
None => nameof(None),
_ => null
};
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static string[] GetNames() => [nameof(V4), nameof(V6), nameof(None)];
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static byte GetNumericValue(IpAddr ipaddr)
{
return ipaddr switch
{
V4 => (byte)IpAddrVariant.V4,
V6 => (byte)IpAddrVariant.V6,
None => (byte)IpAddrVariant.None,
_ => ThrowInvalidType<byte>()
};
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static IpAddrVariant ConvertEnum(IpAddr ipaddr)
{
return ipaddr switch
{
V4 => IpAddrVariant.V4,
V6 => IpAddrVariant.V6,
None => IpAddrVariant.None,
_ => ThrowInvalidType<IpAddrVariant>()
};
}
public static bool TryConvertEnum([NotNullWhen(true)] IpAddr? ipaddr, [MaybeNullWhen(false)] out IpAddrVariant result)
{
switch(ipaddr)
{
case V4:
result = IpAddrVariant.V4;
return true;
case V6:
result = IpAddrVariant.V6;
return true;
case None:
result = IpAddrVariant.None;
return true;
}
result = default;
return false;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static IpAddr Parse(string s, IFormatProvider? provider = default)
{
return Parse(s.AsSpan(), false, provider);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static IpAddr Parse(ReadOnlySpan<char> s, IFormatProvider? provider = default)
{
return Parse(s, false, provider);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static IpAddr Parse(ReadOnlySpan<char> s, bool ignoreCase, IFormatProvider? provider = default)
{
if (TryParse(s, ignoreCase, provider, out var result))
{
return result;
}
else
{
ThrowRequestedValueNotFound(s);
return default!;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool TryParse([NotNullWhen(true)] string? s, IFormatProvider? provider, [MaybeNullWhen(false)] out IpAddr result)
{
return TryParse(s.AsSpan(), false, provider, out result);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool TryParse(ReadOnlySpan<char> s, out IpAddr result)
{
return TryParse(s, false, null, out result);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool TryParse(ReadOnlySpan<char> s, IFormatProvider? provider, [MaybeNullWhen(false)] out IpAddr result)
{
return TryParse(s, false, null, out result);
}
public static bool TryParse(ReadOnlySpan<char> s, bool ignoreCase, IFormatProvider? provider, [MaybeNullWhen(false)] out IpAddr result)
{
if (ignoreCase)
{
if (s.Equals(nameof(V4), StringComparison.OrdinalIgnoreCase))
{
result = V4.Default;
return true;
}
if (s.Equals(nameof(V6), StringComparison.OrdinalIgnoreCase))
{
result = V6.Default;
return true;
}
if (s.Equals(nameof(None), StringComparison.OrdinalIgnoreCase))
{
result = None.Default;
return true;
}
result = default;
return false;
}
else
{
switch (s)
{
case "V4":
result = V4.Default;
return true;
case "V6":
result = V6.Default;
return true;
case "None":
result = None.Default;
return true;
}
result = default;
return false;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool IsDefined(ReadOnlySpan<char> s)
{
return s switch
{
"V4" => true,
"V6" => true,
"None" => true,
_ => false
};
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool IsDefined([NotNullWhen(true)] IpAddr? value)
{
return value switch
{
V4 => true,
V6 => true,
None => true,
_ => false
};
}
[DoesNotReturn]
private static void ThrowRequestedValueNotFound(ReadOnlySpan<char> s)
{
throw new ArgumentException($"Requested value '{s}' was not found.");
}
[DoesNotReturn]
private static T ThrowInvalidType<T>()
{
throw new ArgumentException($"Requested value was invalid type.'");
}
}
</details>
Ignore automatic generation
If automatic generation is to be ignored, you assign the [IgnoreVariant]
attribute.
[IgnoreVariant]
public enum TestVariant
{}
Auto-generated API
Parse TryParse
var v4 = IpAddr.Parse("V4");
var v4IgnoreCase = IpAddr.Parse("v4", true);
if (IpAddr.TryParse("V4", out var v4))
{
Console.WriteLine(v4); // V4 { args0 = 0, args1 = 0, args2 = 0, args3 = 0 }
}
if (IpAddr.TryParse("v4", true, null, out var v4IgnoreCase))
{
Console.WriteLine(v4IgnoreCase); // V4 { args0 = 0, args1 = 0, args2 = 0, args3 = 0 }
}
Count
var count = IpAddr.Count;
Console.WriteLine(count); // 3
GetName
var name = IpAddr.GetName(new IpAddr.V4(127, 0, 0, 1));
Console.WriteLine(name); // V4
GetNames
var names = IpAddr.GetNames();
foreach (var name in names)
{
Console.WriteLine(name);
}
// V4
// V6
// None
IsDefined
var result = IpAddr.IsDefined("V4"); // true
var result2 = IpAddr.IsDefined(new IpAddr.V4(127, 0, 0, 1)); // true
GetNumericValue
var v4Number = IpAddr.GetNumericValue(new IpAddr.V4(127, 0, 0, 1)); // 0
var v6Number = IpAddr.GetNumericValue(new IpAddr.V6("::1")); // 1
ConvertEnum TryConvertEnum
Get the enumeration value of the original enum from the specified member.
IpAddrVariant variantEnum = IpAddr.ConvertEnum(new IpAddr.V4(127, 0, 0, 1));
if (IpAddr.TryConvertEnum(new IpAddr.V4(127, 0, 0, 1), out IpAddrVariant result))
{
}
License
MIT License.
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 |
---|---|---|
1.0.1 | 37 | 1/12/2025 |