Simple utility to wrap your objects in an Option type to avoid and handle common issues with null
.
It is a common pattern seen in languages such as Rust, F# and Haskell in order to force handling of scenarios where a variable might have a value.
This implementation is mimicking the API found in Rust's Option type.
I'll use this class in the examples, but value types such as struct and float also works.
class SomeClass
{
public float Value;
public SomeClass(float value) => Value = value;
}
SomeClass someObject = new SomeClass(10f);
// It supports implicit conversion into an Option
Option<SomeClass> asSome = someObject;
asSome.IsSome() == true;
Option<SomeClass> asNone = null;
asNone.IsNone() == true;
// You can explicitly turn it into `Some`
var asSome = Option<SomeClass>.Some(someObject);
// or create it as `None`
var asNone = Option<SomeClass>.None();
You can check which state an Option
is in
Option<SomeClass> option = new SomeClass(10f);
if (option.IsSome()) {// true}
if (option.IsNone()) {// false}
Several methods exists to unwrap an Option
value, some will throw an exception when the value is None
.
Throws an exception on None
, should obviously be avoided for most use cases
Option<SomeClass> option = new SomeClass(10f);
SomeClass value = option.Unwrap();
Throws an exception, but prefixes it with the given string, useful if you know the value should not be None
.
Option<SomeClass> option = new SomeClass(10f);
SomeClass value = option.Expect("Some descriptive prefix");
Safe unwrap using the Try
pattern.
Option<SomeClass> option = new SomeClass(10f);
if (option.TryUnwrap(out SomeClass value)) { }
Eager Or
, gives the variable passed in the parameter if option is None
.
Use this with caution, as the parameter variable is created even if it's not used.
Option<SomeClass> option = new SomeClass(10f);
SomeClass value = option.UnwrapOr(new SomeClass(0f));
Lazy Or
, uses the delegate pass in the parameter to produce a value if option is None
.
This is the preferred way for producing a value on None
as it is only created when needed.
Option<SomeClass> option = null;
SomeClass value = option.UnwrapOrElse(() => new SomeClass(0f));
Returns default(T)
when option is None
Option<SomeClass> option = null;
SomeClass value = option.UnwrapOrDefault();
Some utility to run a callback when unwrapping a value.
Option<SomeClass> option = new SomeClass(10f);
// Runs when option is `Some`
option.WhenSome(value => { });
// Runs when option is `None`
option.WhenNone(() => { });
// if you want something similar to a `match` statement in Rust you could use this
option.Match(
some: value => {},
none: () => {}
);
// Only run when option is `Some` and the containing value is equal to the one given
option.SomeEqualsThen(new SomeClass(10f), (value) => { });
Some additional methods are supplied for more in-depth use cases
For this example we will map SomeClass
into OtherClass
.
class OtherClass
{
public int Value;
public OtherClass(int value) => Value = value;
}
Option<SomeClass> option = new SomeClass(10f);
Option<OtherClass> = option.Map(origin => {
return Option<OtherClass>.Some((int)origin);
})
Gives the value of other
if both are Some
.
Option<SomeClass> option1 = new SomeClass(10f);
Option<SomeClass> option2 = new SomeClass(20f);
Option<SomeClass> option3 = null;
// This variable will be equal to `option2`
var some = option1.And(option2);
// This variable will be equal to "Option<SomeClass>.None"
var none = option1.And(option2).And(option3);
Gives the first value that is Some
or None
if both are None
Option<SomeClass> option1 = new SomeClass(10f);
Option<SomeClass> option2 = null;
// This will be equal to `option1`
var or = option1.Or(option2);
// This will be equal to `option1` as well
var or = option2.Or(option1);
If value is Some
it will use the given delegate to filter out unwanted values, similar to SomeEqualsThen
but will instead return the Option<T>
object. Also allows you to filter against inner values inside of T
.
Option<SomeClass> option1 = new SomeClass(10f);
// Filtered will be equal to `option1`
var filtered = option1.Filter(value => value.Value == 10f);
// Filtered will be equal to "Option<SomeClass>.None"
var filtered = option1.Filter(value => value.Value == 5f);