| name | csharpessentials-maybe |
| description | Use when representing optional values explicitly — Maybe<T> as a null-safe container, Maybe.From()/FromTry() for creation, HasValue/HasNoValue, Map/Bind chaining, TapNone for None-side effects, Match for consumption, and ToMaybeResult() to bridge into the Result pattern. |
CSharpEssentials.Maybe
Maybe<T> makes optionality explicit. No null reference exceptions — the absence of a value is a first-class concept.
Installation
dotnet add package CSharpEssentials.Maybe
Namespace
using CSharpEssentials.Maybe;
Creating Maybe
Maybe<string> name = Maybe.From(user?.Name);
Maybe<string> none = Maybe<string>.None;
Maybe<string> some = Maybe.From("Alice");
Maybe<string> implicit = user.Name;
Maybe.From(null) → None. Maybe.From(value) → Some(value). Never use .ToMaybe() — that method does not exist.
Checking Value
bool has = maybe.HasValue;
bool empty = maybe.HasNoValue;
string val = maybe.GetValueOrDefault("fallback");
string val = maybe.GetValueOrThrow();
Exception-safe Creation
Maybe<int> n = Maybe<int>.FromTry(() => int.Parse(input));
Maybe<User> u = Maybe<User>.FromTry(() => JsonSerializer.Deserialize<User>(json));
Pattern Match
string result = maybe.Match(
some: name => $"Hello, {name}",
none: () => "Hello, stranger");
Transforming
string display = Maybe.From(user?.Email)
.Map(e => e.ToLowerInvariant())
.GetValueOrDefault("no email");
Maybe<Address> address = Maybe.From(user)
.Bind(u => Maybe.From(u?.Address));
None-side Effects
maybe.TapNone(() => logger.LogWarning("Value was absent"));
await maybe.TapNoneAsync(async () => await NotifyAsync());
await GetMaybeAsync().TapNoneAsync(() => fallback());
int v = maybe.GetValueOrElse(() => ComputeExpensiveDefault());
Maybe<Config> cfg = GetCached().OrElse(() => LoadFromDisk());
await GetCached().OrElseAsync(() => Task.FromResult(LoadFromDisk()));
Bridge to Result
Result<string> r = maybe.ToMaybeResult(
Error.NotFound("user.email", "No email address on file."));
Best Practices
- Use
Maybe.From() — not .ToMaybe() (doesn't exist)
- Prefer
Match() over HasValue + GetValueOrThrow() to avoid branches
- Use
Bind() when the transform itself can be absent (returns Maybe<T>)
- Bridge to
Result with ToMaybeResult() when the caller needs error information