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);