Skip to content

Instantly share code, notes, and snippets.

@aetos382
Last active June 10, 2026 11:12
Show Gist options
  • Select an option

  • Save aetos382/426e249fa269d2d30656eb2fec3948f4 to your computer and use it in GitHub Desktop.

Select an option

Save aetos382/426e249fa269d2d30656eb2fec3948f4 to your computer and use it in GitHub Desktop.
using System;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
TestStandardUnion();
TestCustomUnion1();
TestCustomUnion2();
TestCustomUnion3();
void TestStandardUnion()
{
StandardUnion u = 1;
var underlyingType = u switch
{
int => "int",
string => "string",
_ => "unknown"
};
Console.WriteLine($"union is {underlyingType}.");
}
void TestCustomUnion1()
{
CustomUnion1 u = 1;
var underlyingType = u switch
{
int => "int",
string => "string",
_ => "unknown"
};
Console.WriteLine($"union is {underlyingType}.");
}
void TestCustomUnion2()
{
CustomUnion2 u = 1;
var underlyingType = u switch
{
int => "int",
string => "string",
_ => "unknown"
};
Console.WriteLine($"union is {underlyingType}.");
}
void TestCustomUnion3()
{
CustomUnion3 u = 1;
var underlyingType = u switch
{
int => "int",
string => "string",
_ => "unknown"
};
Console.WriteLine($"union is {underlyingType}.");
}
// boxing する
union StandardUnion(int, string);
// boxing する
// public な 1 引数コンストラクタは union constructor になる
[Union]
readonly struct CustomUnion1 : IUnion
{
public CustomUnion1(int value) => Value = value;
public CustomUnion1(string value) => Value = value;
public object? Value { get; }
}
// boxing しない
// public な 1 引数コンストラクタは union constructor になる
[Union]
readonly struct CustomUnion2 : IUnion
{
private enum ValueKind
{
None,
Int,
String
}
private readonly ValueKind _kind;
private readonly int _intValue;
private readonly string? _stringValue;
public CustomUnion2(int value)
{
_kind = ValueKind.Int;
_intValue = value;
}
public CustomUnion2(string value)
{
_kind = ValueKind.String;
_stringValue = value;
}
public bool TryGetValue(out int value)
{
if (_kind != ValueKind.Int)
{
value = 0;
return false;
}
value = _intValue;
return true;
}
public bool TryGetValue([MaybeNullWhen(false)] out string value)
{
if (_kind != ValueKind.String)
{
value = null;
return false;
}
value = _stringValue!;
return true;
}
public object? Value =>
_kind switch
{
ValueKind.None => null,
ValueKind.Int => _intValue,
ValueKind.String => _stringValue,
_ => throw new UnreachableException()
};
}
// boxing しない
// IUnionMembers.Create に定義された型変換のみが union constructor になる
[Union]
readonly struct CustomUnion3 : IUnion, CustomUnion3.IUnionMembers
{
public interface IUnionMembers
{
static abstract CustomUnion3 Create(int value);
static abstract CustomUnion3 Create(string value);
bool TryGetValue(out int value);
bool TryGetValue([MaybeNullWhen(false)] out string value);
object? Value { get; }
}
private enum ValueKind
{
None,
Int,
String
}
private readonly ValueKind _kind;
private readonly int _intValue;
private readonly string? _stringValue;
public static CustomUnion3 Create(int value) => new(value);
public static CustomUnion3 Create(string value) => new(value);
// これは union constructor ではない
// CustomUnion3 u = new CustomUnion3(1.0); として呼び出すことは可能
// CustomUnion3 u = 1.0; は不可能
public CustomUnion3(double value)
{
}
private CustomUnion3(int value)
{
_kind = ValueKind.Int;
_intValue = value;
}
private CustomUnion3(string value)
{
_kind = ValueKind.String;
_stringValue = value;
}
public bool TryGetValue(out int value)
{
if (_kind != ValueKind.Int)
{
value = 0;
return false;
}
value = _intValue;
return true;
}
public bool TryGetValue([MaybeNullWhen(false)] out string value)
{
if (_kind != ValueKind.String)
{
value = null;
return false;
}
value = _stringValue!;
return true;
}
public object? Value => _kind switch
{
ValueKind.None => null,
ValueKind.Int => _intValue,
ValueKind.String => _stringValue,
_ => throw new UnreachableException()
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment