Skip to content

Instantly share code, notes, and snippets.

@shanecelis
Last active October 16, 2022 22:42
Show Gist options
  • Save shanecelis/ee16908f7c00ce4a7aeb55c4bf3477fc to your computer and use it in GitHub Desktop.
Save shanecelis/ee16908f7c00ce4a7aeb55c4bf3477fc to your computer and use it in GitHub Desktop.
"Use stackoverflow.com code? Yes. Ever cite it? Usually I just add a link but here it is formally. #programming"—@shanecelis
/*
Original code Copyright (c) 2011 cdiggins[1]
Modified code Copyright (c) 2016 Shane Celis, @shanecelis[2]
Licensed under the CC-BY-SA 3.0[3]
Original code posted to this question[4] and answer[5] from
stackoverflow.com where user contributions are licensed under
CC-BY-SA 3.0 with attribution required.
[1]: https://stackoverflow.com/users/184528/cdiggins
[2]: https://twitter.com/shanecelis
[3]: http://creativecommons.org/licenses/by-sa/3.0/
[4]: http://stackoverflow.com/questions/3151702/discriminated-union-in-c-sharp
[5]: http://stackoverflow.com/a/7302957/6454690
*/
/*
CHANGES
-------
May 21, 2016
[ENHANCEMENTS]
* Removed 'dynamic' keyword and made other small changes to support
Unity. (Could be considered a degradation rather than an
enhancement.)
[BUG FIXES]
* Now works with non-static delegates.
* Throws exception on `Match<void>` to stop Unity Editor from crashing.
*/
using System;
using UnityEngine.Assertions;
public class UnionBase<A>
{
object value;
public UnionBase(A a) { value = a; }
protected UnionBase(object x) { value = x; }
protected T InternalMatch<T>(params Delegate[] ds)
{
var vt = value.GetType();
foreach (var d in ds)
{
var mi = d.Method;
// These are always true if InternalMatch is used correctly.
Assert.IsTrue(mi.GetParameters().Length == 1);
Assert.IsTrue(typeof(T).IsAssignableFrom(mi.ReturnType));
//UnityEngine.Debug.Log("declaring type " + mi.DeclaringType);
var pt = mi.GetParameters()[0].ParameterType;
if (pt.IsAssignableFrom(vt)) {
if (typeof(T) != typeof(void)) {
/* If it's not static, we need to use the delegate's target. -sec */
return (T) mi.Invoke(d.Target, new object[] { value });
} else {
throw new Exception("Cannot return void; consider using object.");
}
}
}
throw new Exception("No appropriate matching function was provided");
}
public T Match<T>(Func<A, T> fa) { return InternalMatch<T>(fa); }
}
public class Union<A, B> : UnionBase<A>
{
public Union(A a) : base(a) { }
public Union(B b) : base(b) { }
protected Union(object x) : base(x) { }
public T Match<T>(Func<A, T> fa, Func<B, T> fb) { return InternalMatch<T>(fa, fb); }
}
public class Union<A, B, C> : Union<A, B>
{
public Union(A a) : base(a) { }
public Union(B b) : base(b) { }
public Union(C c) : base(c) { }
protected Union(object x) : base(x) { }
public T Match<T>(Func<A, T> fa, Func<B, T> fb, Func<C, T> fc) { return InternalMatch<T>(fa, fb, fc); }
}
public class Union<A, B, C, D> : Union<A, B, C>
{
public Union(A a) : base(a) { }
public Union(B b) : base(b) { }
public Union(C c) : base(c) { }
public Union(D d) : base(d) { }
protected Union(object x) : base(x) { }
public T Match<T>(Func<A, T> fa, Func<B, T> fb, Func<C, T> fc, Func<D, T> fd) { return InternalMatch<T>(fa, fb, fc, fd); }
}
public class Union<A, B, C, D, E> : Union<A, B, C, D>
{
public Union(A a) : base(a) { }
public Union(B b) : base(b) { }
public Union(C c) : base(c) { }
public Union(D d) : base(d) { }
public Union(E e) : base(e) { }
protected Union(object x) : base(x) { }
public T Match<T>(Func<A, T> fa, Func<B, T> fb, Func<C, T> fc, Func<D, T> fd, Func<E, T> fe) { return InternalMatch<T>(fa, fb, fc, fd, fe); }
}
// public class DiscriminatedUnionTest : IExample
// {
// public Union<int, bool, string, int[]> MakeUnion(int n)
// {
// return new Union<int, bool, string, int[]>(n);
// }
// public Union<int, bool, string, int[]> MakeUnion(bool b)
// {
// return new Union<int, bool, string, int[]>(b);
// }
// public Union<int, bool, string, int[]> MakeUnion(string s)
// {
// return new Union<int, bool, string, int[]>(s);
// }
// public Union<int, bool, string, int[]> MakeUnion(params int[] xs)
// {
// return new Union<int, bool, string, int[]>(xs);
// }
// public void Print(Union<int, bool, string, int[]> union)
// {
// var text = union.Match(
// n => "This is an int " + n.ToString(),
// b => "This is a boolean " + b.ToString(),
// s => "This is a string" + s,
// xs => "This is an array of ints " + String.Join(", ", xs));
// Console.WriteLine(text);
// }
// public void Run()
// {
// Print(MakeUnion(1));
// Print(MakeUnion(true));
// Print(MakeUnion("forty-two"));
// Print(MakeUnion(0, 1, 1, 2, 3, 5, 8));
// }
// }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment