I see the very great advantage of turning on (non-)nullable reference types, but I have quite a lot of methods with optional parameters and I am wondering what the right way is to correct the warnings yielded by the compiler in a wise way.
Making the parameter nullable by annotating the type with "?" takes all the goodness away. Another idea is to turn all methods with optional parameters into separate methods which is quite a lot of work or yields high complexity (exponential explosion of parameter combinations).
I was thinking about something like this, but I really question if that it a good approach (performance-wise etc.) beyond the first glance:
[Fact]
public void Test()
{
Assert.Equal("nothing", Helper().ValueOrFallbackTo("nothing"));
Assert.Equal("foo", Helper("foo").ValueOrFallbackTo("whatever"));
}
public static Optional<string> Helper(Optional<string> x = default)
{
return x;
}
public readonly ref struct Optional<T>
{
private readonly bool initialized;
private readonly T value;
public Optional(T value)
{
initialized = true;
this.value = value;
}
public T ValueOrFallbackTo(T fallbackValue)
{
return initialized ? value : fallbackValue;
}
public static implicit operator Optional<T>(T value)
{
return new Optional<T>(value);
}
}
This look's like F#'s Option. This can be emulated in C# 8 up to a point with pattern matching expressions. This struct :
Should allow code like this :
The first option uses property pattern matching to check for
None
, while the second one uses positional pattern matching to actually extract the value through the deconstructor.The nice thing is that the compiler recognizes this as an exhaustive match so we don't need to add a default clause.
Unfortunately, a Roslyn bug prevents this. The linked issue actually tries to create an Option class based on an abstract base class. This was fixed in VS 2019 16.4 Preview 1.
The fixed compiler allows us to omit the parameter or pass a
None
:This produces :
VS 2019 16.4 should come out at the same time as .NET Core 3.1 in a few weeks.
Until then, an uglier solution could be to return
IsSome
in the deconstructor and use positional pattern matching in both cases:And
Borrowing from F# Options
No matter which technique we use, we can add extension methods to the
Option
static class that mimic F#'s Option module, eg Bind, perhaps the most useful method, applies a function to an Option if it has a value and returns an Option, or returns None if there's no value :For example this applies the
Format
method to an Option to create a Optino :This makes it easy to create other helper functions, or chain functions that produce options