A Option type for .net that prevents using null or "magic values" (NullObject, exit code -1, index out of range, etc.) in your code. Licensed under the MIT License (http://opensource.org/licenses/MIT).
Reading the content type of a url as string and printing it to the console. If the safe cast to HttpWebRequest would return null the subsequent calls to Select and Where would not execute and "No matching result" would be printed to the console. The same would happen if any of the calls to Select would return null or the predicate contentType.StartsWith("text") would not hold in the Where predicate.
Option.From(WebRequest.Create(Url) as HttpWebRequest)
.Select(request => request.GetResponse()?.ContentType)
.Where(contentType => contentType.StartsWith("text"))
.Match(
Some: contentType => Console.WriteLine($"Content-Type: {contentType}"),
None: () => Console.WriteLine("No matching result."));The same can be written using LINQ syntax:
var maybeText =
from req in Option.From(WebRequest.Create(Url) as HttpWebRequest)
let contentType = req.GetResponse()?.ContentType
where contentType.StartsWith("text")
select contentType;
maybeText.Match(
Some: contentType => Console.WriteLine($"Content-Type: {contentType}"),
None: () => Console.WriteLine("No matching result."));The MIT License
Did you find a bug or have an idea for a new feature? Open a new Issue.
- Read the Code of Conduct
- Take a look at the Issues. If there is one you want to work on (has labels
readyandup-for-grabs), write a comment that you want to work on it. If you have a new idea/problem, please open an issue and explain it. - Fork the repo and clone it on your machine.
- Build the project running
build.cmdon Windows orbuild.shon Mac OSX/Linux. - Write your code and don't forget to add xml docs and tests.
- Run the
build.*for your platform again to ensure the build works and all tests pass. - Describe your feature in the README, if applicable (e.g. new combinator).
- Create a pull request.
This project uses SemVer compatible versioning, which means Breaking.Feature.Fix.
Breaking: Changes that break API compatibility with earlier versions.Feature: Added functionality that don't break API compatibility with earlier versions.Fix: Backwards compatible bugfixes and refactoring/clean up.
If a feature becomes deprecated it is marked with the Obsolete attribute and will not throw a compiler error.
In the next release it will throw a compiler error and the major version is incremented by 1 because of breaking changes.
In the release after that, the feature will be removed and its patch version is incremented by 1.
- 3.2.0
[Obsolete("This method is deprecated and will be removed in 2 releases.")]
public bool TryGet(out value) => // ...- 4.0.0
[Obsolete("This method is deprecated and will be removed in the next release.", true)]
public bool TryGet(out value) => // ...- 4.0.1
// TryGet removedOption<T> represents the absence or presence of a value. If a Option<T> contains a value, we'll call it Some from now on. If it is empty, we'll call it None.
Option<int> result = Option.From(2);The above example would evaluate 2 and - because that is a valid integer - return a Some and store it in the variable result. The result of the calculation is stored inside the Some and can be accessed using the Match, IfSome, several Get* or TryGet(DEPRECATED) methods:
// using the Match method
int two;
result.Match(
None: () => { /* no value present */ },
Some: i => two = i);
// or using IfSome
int two;
result.IfSome(i => two = i);
// or using the deprecated TryGet method
int two;
if(result.TryGet(out two))
/* do something with two */
else
/* no value present */To find out if result represents a Some or None it provides the boolean properties IsSome and IsNone:
if(result.IsSome)
// do something;if(result.IsNone)
// do something;Option.From(2).Match(
Some: i => { /* do something with the value */ },
None: () => { /* no value present */ });Match allows to use a pattern matching like callback registration. The first function parameter is only executed in case of a Some and gets the value to work with. The second function parameter is registered to handle None and is only executed if the value is null.
string result = Option.From(2).Match(
Some: i => i.ToString(),
None: () => "");This overload for Match produces a value. In the above example result would be the string "2".
Option.From(someString).IfSome(value => /* Do something with the value */);IfSome is only executed if someString is not null and delegates the value of the Some to its enclosed callback.
Option.From<string>(null).IfNone(() => /* No value present */);IfNone is only executed if None was returned and then executes its enclosed callback.
Every method that may not return a value in some circumstances should return an Option<T> instead of the result directly. That way, eventual null references or the usage of "magic values" can be avoided.
Optional parameters can also be expressed more clearly with an Option<T> instead of some arbitrary value that has to be checked inside the method, which can easily be forgotten. And you don't have to place them last in the parameter list.
So instead of this:
string DoSomethingThatCouldReturnNull(string arg) { /* ... */ }
int DoSomethingThatCouldReturnAMagicValue(string arg) { /* ... */ }
void DoSomethingWithAnOptionalParameter(double wouldBeSecond, int? wouldBeFirst = null) { /* ... */ }write this:
Option<string> DoSomethingThatCouldReturnNull(string arg) { /* ... */ }
Option<int> DoSomethingThatCouldReturnAMagicValue(string arg) { /* ... */ }
void DoSomethingWithAnOptionalParameter(Option<int> first, double second) { /* ... */ }There are several ways to create an Option<T>.
Option<int> option = Option.From(2);
// same as
Option<int> option = 2;Evaluates a T synchronously and returns a Some if the value is not null or None otherwise. Always returns Somefor non-nullable (value) types.
DateTime? now = DateTime.Now;
Option<DateTime> option = Option.From(now);Evaluates a T? synchronously and returns a Some if the value is not null or None otherwise.
Option<double> option = Option.FromTryPattern<string, double>(Double.TryParse, "2.6");Evaluates the call to a given method that follows the TryParse pattern and arguments synchronously and returns a Some if the method succeeded or None otherwise.
Currently the method is overloaded with versions that take up to 16 args.
Option<int> none = Option.None;
// same as
Option<int> none = Option<int>.None;Returns None that represents the absence of a value.
Option<int> none = 3.ToOption();Converts any value to an Option<T> by calling Option.From on it.
int? source = 3;
Option<CustomType> value = source.ToOptionMapped(CustomType.Create);Converts a nullable struct to Option<CustomType> (where CustomType is a wrapper for T with a factory method Create(T)) using given map function.
int source = 3;
Option<CustomType> value = source.ToOptionMappedOrNoneIf(0, CustomType.Create);Converts a struct to Option<CustomType> (where CustomType is a wrapper for T with a factory method Create(T)) using given map function. If given source value equals given magic value for none, Option.None ist returned.
C# 6 offers the feature to statically import classes and use the static methods therein without having to prefix them with the class name. NeverNull provides a specific module for taking advantage of this feature.
using static NeverNull.Predef
Option<int> two = Option(2);
// or
Option<int> two = Some(2);
// io for None
Option<int> none = None;Since Some and None are simple data structures, a couple of extension methods are provided that make working with both types easier.
bool containsFive = Option.From(5).Contains(5);Returns true if the Option it is applied to is a Some containing the desired value otherwise false.
Option<int?> option = Option.From<int?>(5);
Option<int> normalized = option.Normalize();Normalizes an Option<T?> into its Option<T> representation.
Option<DateTime> nowOption = Option.From(DateTime.Now)
DateTime? maybeNow = nowOption.ToNullable();Converts an Option<T> (where T is a value type) to a Nullable<T>.
Option<CustomType> source = CustomType.Create(3).ToOption();
int? result = source.ToNullable(ct => ct.Value);Converts an Option<CustomType> (where CustomType is a wrapper for a T with property public T Value {get;}) to a Nullable<T>.
int two = Option.From(2).Get();Get is the most straight forward extension. It returns the value if the result is a Some or throws an InvalidOperationException, if None. In the above example two would be 2.
int two = Option.From(2).GetOrElse(-1);
// or lazily with a func
int two = Option.From(2).GetOrElse(() => -1);GetOrElse either returns the value, if From returned a Some or the else value, if From returned None. In the above example two would be 2. It would have been -1 if 2 was null.
int two = Option.From(2).GetOrDefault();GetOrDefault either returns the value, if From returned a Some or the value of default(T), if From returned None. In the above example two would be 2. It would have been 0 which is default(int) if 2 was null.
Option<int> result = Option.From<int?>(null).OrElse(-1);
// or lazily with a func
Option<int> result = Option.From<int?>(null).OrElse(() => -1);In the above examples null would have been returned and result would be None. The OrElse combinator makes it possible to return a different value in case of None. result would be a Some<int> with the Value -1.
Option<int> result = Option.From<int?>(null).OrElseWith(Option.From(-1));
// or lazily with a func
Option<int> result = Option.From<int?>(null).OrElseWith(() => Option.From(-1));In the above examples null would have been returned and result would be None. The OrElseWith combinator makes it possible to return a different Option in case of None. result would be a Some<int> with the Value -1.
Option<string> result = Option.From(2).Select(i => i.ToString());Select allows to apply a function to the value of a Some. In the above example result would contain the string value "5".
Option<string> result = Option.From(2).SelectMany(i => Option.From(i.ToString()));SelectMany allows to apply a function to the value of a Some that returns another Option and avoid the nesting that would occur otherwise. In the above example result would be a Some with Value "5". If Some would have been used, result would have been a Option<Option<string>>.
var result = Option.From(5).Where(i => i == 5);Where checks if a given predicate holds true for an Option. In the above example result would be a Some with Value 5. If the predicate i => i == 5 would not hold, result would have been None.
var result = Option.From(5).Reject(i => i == 5);Reject does the exact opposite of Where. In the above example result would be None. If the predicate i => i == 5 would not hold, result would have been a Some with value 5.
Option<int> five = 5;
Option<int> four = 4;
Option<int> nine = five.Zip(four, (a, b) => a + b);Takes another Option and allows to apply a Func to the values of both options. If one Option would be None the function would not be applied.
In the above example nine would be a Some containing 9.
Option<int> five = 5;
Option<int> four = 4;
Option<int> nine = five.ZipWith(four, (a, b) => Option.From(a + b));Takes another Option and allows to apply a Func to the values of both options. If one Option would be None the function would not be applied.
In the above example nine would be a Some containing 9.
Option<string> result = Option.From(2).Transform(
Some: i => i.ToString(),
None: () => "");Can be used to transform the value of an Option. The first function parameter transforms the resulting value if it is a Some, the second returns a value if it is None. In the above example result would be a Some with value "5".
Option<string> result = Option.From(2)
.Do(i => Console.Write($"Do executed on {i}"))
.Select(i => i.ToString());Can be used to execute side effecting behavior without modifying an Option. In the above example result would be a Some containing "2". The given Action will be executed in either case, regardless if the incoming Option is a Some or None.
Option<Option<int>> nestedOption = Option.From(Option.From(2));
Option<int> result = nestedOption.Flatten();Flattens a nested Option.
Option<int?> two = Option.From(2);
Option<int?> nil = Option.From<int?>(null);
Option<int?> three = Option.From(3);
Option<string> result = nil.Switch(three, two);Returns the first Option that contains a value or None if all are None.
NeverNull contains several extensions that integrate Option<T> with IEnumerable<T>.
IEnumerable<int?> ints = new [] { 1, 2, default(int?), 4 };
IEnumerable<Option<int>> optionalInts = Option.From(ints).Exchange();If the given Option<IEnumerable<T>> is a Some, Option.From is applied to all values of the enumerable. If it is None instead, an empty enumerable is returned.
IEnumerable<Option<int?>> ints = new[] {1, 2, default(int?), 4};
IEnumerable<int> values = ints.SelectValues();Select only the values contained in the options of the given enumerable. If it only contains None, an empty enumerable is returned.
IEnumerable<string> strings = new[] {"a", "b", default(string), "d"};
Option<string> abd = strings.AggregateOptional((a, c) => a + c);Like the standard Linq Aggregate but automatically handles null elements and results in an option.
IEnumerable<int?> ints = new[] {1, 2, default(int?), 4};
Option<int> seven = ints.AggregateOptional((a, c) => a + c);Like AggregateOptional but for Nullable<T> elements.
IEnumerable<Option<string>> strings = new[] {"a", "b", default(string), "d"};
Option<IEnumerable<string>> abd = strings.AllOrNone();Returns an option containing all values or None, if any of the options in the enumerable does not contain a value or the enumerable is empty.
IEnumerable<string> strings = new[] {"a", "b", default(string), "d"};
Option<string> a = strings.FirstOptional();Returns a Some containing the first value if the enumerable contains at least one value, else None.
IEnumerable<string> strings = new[] {"a", "b", default(string), "d"};
Option<string> d = strings.LastOptional();Returns a Some containing the last value if the enumerable contains at least one value, else None.
IEnumerable<string> strings = new[] {"a"};
Option<string> a = strings.SingleOptional();Like Single but returns the only element in the enumerable wrapped in an option. If this enumerable is empty or the single element is NULL, None is returned. Throws an exception if this enumerable contains more than one element.
IEnumerable<string> strings = new[] {"a"};
Option<string> a = strings.SingleOptional();Like SingleOptional but for Nullable<T> elements.