Skip to content

Instantly share code, notes, and snippets.

@kekyo
Last active July 21, 2023 08:28
Show Gist options
  • Save kekyo/68bfa6a23b26b1a7ff2a1077525ba4d4 to your computer and use it in GitHub Desktop.
Save kekyo/68bfa6a23b26b1a7ff2a1077525ba4d4 to your computer and use it in GitHub Desktop.
Hacked Option<T> enabling pattern matching on C#
// Copyright (c) Kouji Matsui (@[email protected])
// License under MIT.
using System.ComponentModel;
namespace TestApp;
public static class Option
{
public static readonly NoneOption None =
NoneOption.Value;
public static Option<T> Some<T>(T value) =>
new SomeOption<T>(value);
}
[EditorBrowsable(EditorBrowsableState.Never)]
public sealed class NoneOption : None
{
private NoneOption()
{
}
public override string ToString() =>
"None";
public static readonly NoneOption Value =
new NoneOption();
}
public abstract class Option<T>
{
public static implicit operator Option<T>(NoneOption none) =>
NoneOption<T>.Value;
}
public interface Some
{
void Deconstruct(out object? value);
}
public interface Some<T>
{
void Deconstruct(out T value);
}
public interface None
{
}
internal sealed class SomeOption<T> : Option<T>, Some<T>, Some
{
private readonly T value;
public SomeOption(T value) =>
this.value = value;
void Some.Deconstruct(out object? value) =>
value = this.value;
void Some<T>.Deconstruct(out T value) =>
value = this.value;
public override string ToString() =>
$"Some {(this.value is string str ? $"\"{str}\"" : this.value?.ToString() ?? "(null)")}";
}
internal sealed class NoneOption<T> : Option<T>, None
{
public NoneOption()
{
}
public override string ToString() =>
"None";
public static readonly Option<T> Value =
new NoneOption<T>();
}
// =====================================================================
public static Option<string> FooBar()
{
return Option.Some("ABC");
return Option.None;
}
switch (FooBar())
{
case Some(var str):
// ...
break;
case None:
// ...
break;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment