type:
We have Java at home :^)
// Go upvote https://stackoverflow.com/a/3199453
public abstract class Union<A, B>
{
// Casts
public static implicit operator Union<A, B>(A value) => new Ayy(value);
public static implicit operator Union<A, B>(B value) => new Bee(value);
public static explicit operator A(Union<A, B> union) => union.Match(
a => a,
b => throw new InvalidCastException($"Cannot cast union of underlying type {typeof(A)} to {typeof(B)}")
);
public static explicit operator B(Union<A, B> union) => union.Match(
a => throw new InvalidCastException($"Cannot cast union of underlying type {typeof(B)} to {typeof(A)}"),
b => b
);
// Interface
public abstract T Match<T>(
Func<A, T> matchA,
Func<B, T> matchB
);
private Union()
{
}
// Convenience
public bool IsA => Match(
a => true,
b => false
);
public bool IsB => Match(
a => false,
b => true
);
public void Select(
Action<A> selectA,
Action<B> selectB
) {
Match(
a => { selectA(a); return false; },
b => { selectB(b); return false; }
);
}
// Implementation of A
public sealed class Ayy : Union<A, B>
{
private A value;
public Ayy(A value)
{
this.value = value;
}
public override T Match<T>(
Func<A, T> matchA,
Func<B, T> _
) => matchA(this.value);
}
// Implementation of B
public sealed class Bee : Union<A, B>
{
private B value;
public Bee(B value)
{
this.value = value;
}
public override T Match<T>(
Func<A, T> _,
Func<B, T> matchB
) => matchB(this.value);
}
}
Some example usage:
var a = new Union<string, int>.Ayy("hello"); var b = new Union<string, int>.Bee(123); Union<string, int> c = "world"; Union<string, int> d = 456; void PrintUnion(Union<string, int> union) => Console.WriteLine(union.Match( a => $"\"{a}\"", b => $"{b}i" )); PrintUnion(a); PrintUnion(b); PrintUnion(c); PrintUnion(d); Console.WriteLine((string)a); try { Console.WriteLine((string)b); } catch (Exception ex) { Console.Error.WriteLine($"Caught exception: {ex}"); }
Console.WriteLine(b.IsA ? $"\"{(string)b}\"" : $"{(int)b}");
string? lastString = null;
int? lastInt = null; foreach (var onion in new Union<string, int>[] { a, b, c, d }) { onion.Select( str => { lastString = str; }, intg => { lastInt = intg; } ); } Console.WriteLine($"Last string: {lastString}, last int: {lastInt}");