Skip to content

Library for providing optionals similar to java but with the possibility to add messages and exceptions to the output

License

Notifications You must be signed in to change notification settings

lunardoggo/Optionals

Repository files navigation

LunarDoggo.Optionals


License Nuget Nuget

This library was inspired by a project at university where we had to implement an extension to Java optionals. C# provides an alternative to optionals with the null-conditional (?) and null-coalescing (??) operators, but there is no easy way to return error messages to calling methods by using these operators without throwing Exceptions or returning custom objects that can contain a value or an error message.

This library's main goal is to provide an easy way for bubbling exceptions and messages up to calling methods without needing an uncountable amount of try-catch-blocks and if-statements. To achieve this goal, the library distinguishes between three kinds of optionals:

  • those which contain values
  • those which contain only a message
  • those which contain a message and an exception

Usage


1. Creating optionals

using LunarDoggo.Optionals;
[...]
void SomeMethod()
{
    //Create an optional containing a value
    IOptional<int> value = Optional.OfValue<int>(12);
    
    //Create an optional containing a message
    IOptional<int> message = Optional.OfMessage<int>("Insert your message here");
    
    //Create an optional containing an exception
    IOptional<int> exception = Optional.OfException<int>(new ArgumentException("Some message"));
    
    //Create an optional containing an exception and a custom message
    IOptional<int> exception = Optional.OfException<int>(new ArgumentException("Some exception"), "Custom message");
    
    //Create an optional from a collection of optionals of the same generic type
    IOptional<IEnumerable<int>> values = Optional.OfOptionals<IEnumerable<int>>(new[] { Optional.OfValue<int>(1), [...] });
}

Optional.OfMessage<T>(string), Optional.OfException<T>(Exception) and Optional.OfException<T>(Exception, string) are mainly used when mapping an optional using the methods IOptional<T>.FlatMap<S>(Func<T, IOptional<S>>) and IOptional<T>.SafeFlatMap<S, V>(Func<T, IOptional<S>>) in order to

2. Map between different types of optionals

using LunarDoggo.Optionals;
[...]
void SomeMethod()
{
    IOptional<string> value = Optional.Of<string>("123");
    
    //Map to int
    IOptional<int> map = value.Map(_str => Int32.Parse(_str)); //does not catch exceptions
    IOptional<int> safeMap = value.SafeMap<int, FormatException>(_str => Int32.Parse(_str)); //catches Exceptions of the provided type and returns an IOptional<T> containing the caught Exception if one was caught
    
    //FlatMap to int; useful for example for validation
    IOptional<int> flatMap = value.FlatMap(_str => {
        int i = Int32.Parse(_str);
        if(i > 100)
            return Optional.OfMessage<int>("Expected a value less or equal to 100");
        return Optional.OfValue<int>(i);
    }); //does not catch exceptions
    IOptional<int> safeFlatMap = value.FlatMap<int, FormatException>(_str => {
        int i = Int32.Parse(_str);
        if(i > 100)
            return Optional.OfMessage<int>("Expected a value less or equal to 100");
        return Optional.OfValue<int>(i);
    }); //catches Exceptions of the provided type and returns an IOptional<T> containing the caught Exception if one was caught
}

Please be aware that optionals that contain a message or an Exception will always return an optional containing the same message or Exception, the provided mapping functions will not be executed

3. Apply changes to an optional:

using LunarDoggo.Optionals;
[...]
void SomeMethod()
{
    //Apply something to the contained value (only changes the contained value if the optional contains an object of a mutable reference type)
    IOptional<int[]> values = Optional.OfValue<int[]>(new int[] { 1, 2, 3 }).Apply(_values => _values[1] = 10); //sets the second value in the array contained in the optional to 10
}

Please be aware that optionals that contain a message or an Exception will not execute the provided action

4. Extract values from an optional without using the Value property:

using LunarDoggo.Optionals;
[...]
void SomeMethod()
{
    IOptional<string> value = Optional.Of<string>("123");
    
    //Convert an optional to a string
    string intValue = value.ToString(_str => _str + "!"); //returns "123!" in this example
    string message = Optional.OfMessage<int>("Some message").ToString(_i => _i); //returns "Some message"
    
    //Get the contained value or an alternative
    string val1 = value.OrElse("Empty"); //Returns "123" in this example
    int val2 = Optional.OfMessage<int>("Message").OrElse(123); //returns 123
    int val3 = Optional.OfMessage<int>("Message").OrElse(() => 321); //returns 321
}

API


Methods and properties provided by every IOptional<T> object:

Method/Property Description
bool HasValue { get; } Returns whether the optional contains a value
T Value { get; } Returns the contained value if it is present otherwise, a NotSupportedException is thrown
bool HasMessage { get; } Returns whether the optional contains a message
string Message { get; } Returns the contained message if it is present, otherwise a NotSupportedException is thrown
bool HasException { get; } Returns whether the optional contains an Exception
IOptional<S> SafeFlatMap<S, V : Exception>(Func<T, IOptional<S>>) maps this optional to an optional of type S. Use this version if you want to catch a specific kind of exception
IOptional<S> SafeFlatMap(Func<T, IOptional<S>>) maps this optional to an optional of type S. Use this version if you want to catch any kind of exception
IOptional<S> FlatMap(Func<T, IOptional<S>>) maps this optional to an optional of type S
IOptional<S> SafeMap<S, V : Exception>(Func<T, S>) maps this optional to an optional of type S. Use this version if you want to catch a specific kind of exception
IOptional<S> SafeMap(Func<T, S>) maps this optional to an optional of type S. Use this version if you want to catch any kind of exception
IOptional<S> Map(Func<T, S>) maps this optional to an optional of type S
IOptional<T> Map(Action<T>) applies an Action<T> to the contained value
IOptional<T> IfHasException(Action<Exception>) the provided action is executed if the optional contains an exception
IOptional<T> IfHasMessage(Action<string>) the provided action is executed if the optional contains a message
IOptional<T> IfHasValue(Action<T>) the provided action is executed if the optional contains a value
string ToString(Func<T, string>) converts the contained value to a string if it is present, otherwise the contained message is returned
T OrElseThrow(Exception) Returnes the contained value if it is present, otherwise the provided exception is thrown
T OrElse(Func<T>) Returnes the contained value if it is present, otherwise the result of provided function returning an object of type T is returned
T OrElse(T) Returnes the contained value if it is present, otherwise the provided parameter of type T is returned

Note: the methods IfHasValue and Apply functionally behave the same way, but IfHasValue is intended to call other code if a value is contained, whereas Apply is intended to be used to alter the contained value itself

Methods provided by the static class Optional:

Method Description
IOptional<T> OfValue<T>(T) Creates a new IOptional<T> containing the provided value
IOptional<T> OfMessage<T>(string) Creates a new IOptional<T> containing the provided message
IOptional<T> OfException<T>(Exception) Creates a new IOptional<T> containing the provided Exception
IOptional<T> OfException<T>(Exception, string) Creates a new IOptional<T> containing the provided Exception and custom message
IOptional<IEnumerable<T>> OfOptionals<T>(IEnumerable<IOptional<T>>) Creates a new IOptional<IEnumerable<T>> containing the value of all provided IOptional<T> objects. If any of the provided objects contains an Exception, the resulting optional contains all Exceptions, if any of the provided objects contains a message, all Messages will be concatenated to a single one and an optional containing this message will be returned instead

Extension methods provided by the static class Optional:

Extension method Description
IOptional<IEnumerable<T>> Filter<T>(Func<T, bool>) Filters all values contained in the optional and returns an optional that only contains the items that match the provided filter
IOptional<S> ForEach<S, T>(Action<T>) where S : IEnumerable<T> Applies the provided action to every item of the optional's contained collection
IOptional<S> Convert<T, S, V>(Func<T, S>) Returns an optional that contains the same values as the provided optional but with another kind of collection derived from IEnumerable<V>
IOptional<S> Cast<T, S, V>() Returns an optional that contains the same collection as the calling optional, but with a type cast applied to it

About

Library for providing optionals similar to java but with the possibility to add messages and exceptions to the output

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Languages