Optional return in C#.Net
Asked Answered
N

13

73

Java 1.8 is receiving the Optional<T> class, that allows us to explicitly say when a method may return a null value and "force" its consumer to verify if it is not null (isPresent()) before using it.

I see C# has Nullable<T>, that does something similar, but with basic types. It seems to be used for DB queries, to distinguish when a value exists and is 0 from when it doesn't exist and is null.

But it seems that C#'s Nullable<T>doesn't work for objects, only for basic types, while Java's Optional<T> only works for objects and not for basic types.

Is there a Nullable/Optional class in C#, that forces us to test if object exists before extracting and using it?

Neutralism answered 24/4, 2013 at 18:9 Comment(13)
This is called the Maybe monad. C# doesn't have it.Angeloangelology
But reference types are already nullable, why is this needed?Acropolis
No - the developer must check to see if an object is null (if there's a chance it maybe) before using it. C# does have the null coalescing operator (??) which can be used in an evaluation to return a selected value if the object is null.Duyne
@MikeChristensen I think OP is asking whether there is some interface/contract that forces users to check whether type is null before using it. Note that nullable DOES NOT fulfill this requirement. nullable simply boxes primitive types into objects so that they can be nullable. This seems to be different from the Optional class OP is describingIridescence
@MikeChristensen - Because you can't express that a reference type isn't null. Option<T> makes the possibility of missing values explicit in the type.Japan
For the record C# does not have "basic types" it has "value types" including user defined value types.Telepathy
Premium versions of Visual Studio support static code analysis, and the feature you're looking for is referred to as Code Contracts. See the answer to the following question for more info: #2338173Sage
There's nothing built-in, and while you could define your own Option<T> struct, since it's not standard, you've now added another way of representing a possibly missing value. You would need to convince everyone on your team to use Option<T> and never return null references or use Nullable<T>. You'll also have to deal with converting to and from the existing representation to external code which uses them.Japan
Code Contracts are pretty cool, but they do have some limitations. They will only create compiler warnings, not errors. Also, this analysis is pretty slow on large projects. However, I'm hoping this technology will evolve and eventually be integrated into the language itself. It's incredibly promising!Acropolis
I used to think that C# was the end-all be-all of languages. I love the parameter-property-declarations of TypeScript, the language-integrated code-contracts of spec#, units of measure of f#, purity of Haskell (at least as an optional specification), undefined of javascript. I wish they would all have a love-child. Nothing from T-SQL is allowed in.Vocabulary
Can you use a ref or out parameter instead?Trifle
@Angeloangelology wrapping a value in a container that allows checking if it's present or not does not make a monad structure. a monad requires certain founctions for construction of monadic values and composition of monad-producing functions. there is nothing monadic about the mere concept of wrapping something.Harpoon
To add to the topic much later, C# 8 has a feature called nullable reference types, which despite its name introduces an option to have non-nullable reference types. Basically, it works at the level of static analysis warnings and may be overridden at any time, assigning null to a non-nullable variable. Still, .NET nor C# have no support for the maybe monad.Complex
C
39

Not in the language, no, but you can make your own:

public struct Optional<T>
{
    public bool HasValue { get; private set; }
    private T value;
    public T Value
    {
        get
        {
            if (HasValue)
                return value;
            else
                throw new InvalidOperationException();
        }
    }

    public Optional(T value)
    {
        this.value = value;
        HasValue = true;
    }

    public static explicit operator T(Optional<T> optional)
    {
        return optional.Value;
    }
    public static implicit operator Optional<T>(T value)
    {
        return new Optional<T>(value);
    }

    public override bool Equals(object obj)
    {
        if (obj is Optional<T>)
            return this.Equals((Optional<T>)obj);
        else
            return false;
    }
    public bool Equals(Optional<T> other)
    {
        if (HasValue && other.HasValue)
            return object.Equals(value, other.value);
        else
            return HasValue == other.HasValue;
    }
}

Note that you won't be able to emulate certain behaviors of Nullable<T>, such as the ability to box a nullable value with no value to null, rather than a boxed nullable, as it has special compiler support for that (and a some other) behavior.

Chamfer answered 24/4, 2013 at 18:14 Comment(19)
This wouldn't do any good. The only thing you're achieving with this is that you replace a NullReferenceException with an InvalidOperationException if the developer doesn't do the check. Apart from that you are changing the way he has to check for null. In the end, it doesn't add a lot value. It would be useful if you could force the check, but that's not possible.Rouault
As far as I know this is the case in Java too. Option in languages like Java and C# allows you to express the semantic possibility to return null from the method. There are languages where the compiler will force you to check but not these two. The problem with defining your own type is that only your code will use it and you kind of want to have the whole ecosystem using it.Telepathy
@Rouault The OP didn't ask for a way of avoiding null checks; he asked for a way to wrap the concept of an optional value into a type in C# (for classes as well as structs). This does that.Chamfer
OK, I see your point. I just think that it's not really a good pattern since you're replacing the default way of doing this with another without any real benefits.Rouault
@Rouault Well, one advantage is that you can unify the concept of nullability between structs and classes. Rather than having a different system for each type of object you can use this one system for any type of object. Now, would I use something like this in my programs, no, probably not. I don't see it being worthwhile, but I'm not going to say that the OP mustn't use such a class ever.Chamfer
@Rouault the other thing that this approach gives you is that you can then apply some reasoning about your program. If you use this approach everywhere then you can assume that nothing will ever be null (and even use AOP techniques to enforce that, see Fody.NullGuard) and it then becomes explicit when a return value or parameter is one that can be null, as it will be Optional<T>, and not just some object which you have no way of knowing if it is allowed to be null or not.Underpay
@Telepathy In Java you can use Findbugs to validate the checks, and will show an error in any place where you might be doing a NullPointerException because there is a code path that can make that variable null, and you didn't check for it. Also, the main thing about Optional<T> is the ifPresent method, where you can apply a lambda expresion if not null, making the code way simpler, especially when you chain ifPresent callsIkhnaton
Isn't System.Nullable<T> helpful? msdn.microsoft.com/en-us/library/1t3y8s4s.aspxClaxton
You could throw the exception even if the value isn't null but they didn't call hasvalue previously. This would force the user to check hasvalueDalhousie
@rolls Why in the world would you want to do that? It's just strictly downside. It's not even necessarily correct to always do a null check before getting the value; forcing it is often enough going to be wrong.Chamfer
It was a hackish way to enforce that user checks if something is null before using it. Though you can just use resharper to inform you. I would never do it but it seems like that was they wanted, enforced null checking for anything that can be null.Dalhousie
"Why [...] would you want to do that?" -- to be sure the check is done in the mainline non-null case during development, rather than finding out in the field that there's no check for null. It's not as good as enforcing the check at compile time, but it's the next best thing. "It's not even necessarily correct to always do a null check ..." -- of course it is, which is why a proper Option type enforces that check at compile time. It's apparent that you don't understand the whole concept of Optional and monads: en.wikipedia.org/wiki/Option_type#The_option_monadExhaustion
@JimBalter The whole point is that the Optional class always does do the null check before providing the underlying value, so there's no reason to force the caller to check it again. If the caller wants an exception to be thrown if they dereference a null value, which is the correct behavior in many situations, then there's nothing wrong with that. It's not like they can get a garbage value without realizing it's not a valid value. Did you not even look at the code?Chamfer
Sorry, as I said, you don't understand the concept. Over and out.Exhaustion
@JimBalter So care to explain why doing the check a second time is in any way useful? Why do you think that checking the value once isn't enough? Do you expect it to be different the second time you check the same value?Chamfer
@Chamfer It would be useful if it had methods like forEach and map in it.Abloom
@Abloom That behavior is now backed into the language in the form of the null propagation operators, but sure, if you're going to write your own optional type, you could write such a method.Chamfer
the useful part of Optional in Java comes from the methods like filter, map, etc. which automatically handle if there is no value inside and can return an Empty Optional instead of throwing an exception. at the end of a chain of .filter().map()... you can use isPresent, isEmpty, orElse, etcGestation
I tend to agree that without map, filter, etc. it doesn't provide a ton of value simply because you're trading purely functional pipelines with operators to short circuit the code when it's a None.Humor
J
41

In my opinion, any Option implementation which exposes HasValue property is the defeat of the entire idea. The point of optional objects is that you can make unconditional calls to their contents without testing whether the content is there.

If you have to test whether the optional object contains a value, then you have done nothing new compared to common null tests.

Here is the article in which I am explaining optional objects in full detail: Custom Implementation of the Option/Maybe Type in C#

And here is the GitHub repository with code and examples: https://github.com/zoran-horvat/option

If you're reluctant to use a heavyweight Option solution, then you can easily build a lightweight one. You can make your own Option<T> type which implements IEnumerable<T> interface, so that you can leverage LINQ extension methods to turn calls optional. Here is the simplest possible implementation:

public class Option<T> : IEnumerable<T>
{
    private readonly T[] data;

    private Option(T[] data)
    {
        this.data = data;
    }

    public static Option<T> Create(T value)
    {
        return new Option<T>(new T[] { value });
    }

    public static Option<T> CreateEmpty()
    {
        return new Option<T>(new T[0]);
    }

    public IEnumerator<T> GetEnumerator()
    {
        return ((IEnumerable<T>)this.data).GetEnumerator();
    }

    System.Collections.IEnumerator
        System.Collections.IEnumerable.GetEnumerator()
    {
        return this.data.GetEnumerator();
    }
}

Using this Option<T> type is done via LINQ:

Option<Car> optional = Option<Car>.Create(myCar);
string color = optional
  .Select(car => car.Color.Name)
  .DefaultIfEmpty("<no car>")
  .Single();  // you can call First(), too

You can find more about optional objects in these articles:

And you may refer to my video courses for more details on how to simplify control flow using Option type and other means: Making Your C# Code More Functional and Tactical Design Patterns in .NET: Control Flow

The first video course (Making Your C# Code More Functional) brings detailed introduction to railway-oriented programming, including the Either and Option types and how they can be used to manage optional objects and handle exceptional cases and errors.

Jebel answered 25/9, 2016 at 18:42 Comment(15)
Thanks. The point in using Optional is that, when some software error lets an object method or field be called on a pointer that's null, it will throw an Exception. This kind of error happens when somebody forgets to test the pointer for null before using it, and compiler lets the call be compiled without a test.Neutralism
Optional makes forgetting it less possible, because it has an interface for testing if the value is available (.hasValue) and to accessing it (.value), and the actual object is only used when accessed. This way, if somebody receives an object as return or parameter and tries to use it directly, compiler will red an error and developer will remember to test for null.Neutralism
And the Optional object must never be null. Java implementation lets it be null, so it's not good enough.Neutralism
My point is that you never test whether Optional contains anything. Just call the method and let Optional forward the call to the contained object if it contains anything. That is how you turn an optional call, call wrapped in an if-else, into an unconditional call on an optional object.Jebel
@ZoranHorvat thanks for this answer. Might you consider adding a usage example? I don't understand how an enumerable is a successful placeholder for a single value.Bardo
@Bardo This implementation from the answer is the basic one and it relies on LINQ for everything beyond constructing the Option. In another article I have implemented a complete Option type with full support for related operations. The article also contains GitHub repo link with the entire code. Here is the article: codinghelmet.com/?path=howto/…Jebel
@ZoranHorvat Since I didn't find the complete implementation (i.e. heavyweight version) of your option pattern on NuGet. I implemented it and I made it available. My code is mostly inspired by your course Making Your C# Code More Object-oriented. The code can be found here and the NuGet package can be downloaded here.Houseboat
Got a compiler error on string color = optional, use "var color" instead?Footrest
@Footrest var can't fix the compile error, it can only change the underlying type. What is the type of the expression you're assigning to string?Jebel
@ZoranHorvat, it's hard to post code in comment, I created an answer below. the color is IEnumerable<string>, a bit inconvenient when using it.Footrest
@Footrest Ah, I see - the reduction call was missing above (now I've fixed it). It's not the point to collect a sequence of strings as the result, but a single string. Thank you for pointing out!Jebel
@ZoranHorvat, I changed my code a little bit. hopefully I get your point this time.Footrest
@Footrest Yes, that is it.Jebel
@ZoranHorvat - your static operator == and operator != overloads defined in the Some and None classes don't appear to work (testing in your project in VS2017 and VS2019), so instead I defined them once in the base Option class (with the type name changed accordingly) and removed the definitions from the derived Some and None classes, which is working.Voluble
"If you have to test whether the optional object contains a value, then you have done nothing new compared to common null tests." Not necessarily. Seems most people look at Option in a vacuum. Any useful implementation would include Map, Filter, etc in which case the Some check is encapsulated in the type itself; how do you do that cleanly with a myriad of null checks scattered everywhere in your code and what do you do about it when it is null? Option covers that.Humor
C
39

Not in the language, no, but you can make your own:

public struct Optional<T>
{
    public bool HasValue { get; private set; }
    private T value;
    public T Value
    {
        get
        {
            if (HasValue)
                return value;
            else
                throw new InvalidOperationException();
        }
    }

    public Optional(T value)
    {
        this.value = value;
        HasValue = true;
    }

    public static explicit operator T(Optional<T> optional)
    {
        return optional.Value;
    }
    public static implicit operator Optional<T>(T value)
    {
        return new Optional<T>(value);
    }

    public override bool Equals(object obj)
    {
        if (obj is Optional<T>)
            return this.Equals((Optional<T>)obj);
        else
            return false;
    }
    public bool Equals(Optional<T> other)
    {
        if (HasValue && other.HasValue)
            return object.Equals(value, other.value);
        else
            return HasValue == other.HasValue;
    }
}

Note that you won't be able to emulate certain behaviors of Nullable<T>, such as the ability to box a nullable value with no value to null, rather than a boxed nullable, as it has special compiler support for that (and a some other) behavior.

Chamfer answered 24/4, 2013 at 18:14 Comment(19)
This wouldn't do any good. The only thing you're achieving with this is that you replace a NullReferenceException with an InvalidOperationException if the developer doesn't do the check. Apart from that you are changing the way he has to check for null. In the end, it doesn't add a lot value. It would be useful if you could force the check, but that's not possible.Rouault
As far as I know this is the case in Java too. Option in languages like Java and C# allows you to express the semantic possibility to return null from the method. There are languages where the compiler will force you to check but not these two. The problem with defining your own type is that only your code will use it and you kind of want to have the whole ecosystem using it.Telepathy
@Rouault The OP didn't ask for a way of avoiding null checks; he asked for a way to wrap the concept of an optional value into a type in C# (for classes as well as structs). This does that.Chamfer
OK, I see your point. I just think that it's not really a good pattern since you're replacing the default way of doing this with another without any real benefits.Rouault
@Rouault Well, one advantage is that you can unify the concept of nullability between structs and classes. Rather than having a different system for each type of object you can use this one system for any type of object. Now, would I use something like this in my programs, no, probably not. I don't see it being worthwhile, but I'm not going to say that the OP mustn't use such a class ever.Chamfer
@Rouault the other thing that this approach gives you is that you can then apply some reasoning about your program. If you use this approach everywhere then you can assume that nothing will ever be null (and even use AOP techniques to enforce that, see Fody.NullGuard) and it then becomes explicit when a return value or parameter is one that can be null, as it will be Optional<T>, and not just some object which you have no way of knowing if it is allowed to be null or not.Underpay
@Telepathy In Java you can use Findbugs to validate the checks, and will show an error in any place where you might be doing a NullPointerException because there is a code path that can make that variable null, and you didn't check for it. Also, the main thing about Optional<T> is the ifPresent method, where you can apply a lambda expresion if not null, making the code way simpler, especially when you chain ifPresent callsIkhnaton
Isn't System.Nullable<T> helpful? msdn.microsoft.com/en-us/library/1t3y8s4s.aspxClaxton
You could throw the exception even if the value isn't null but they didn't call hasvalue previously. This would force the user to check hasvalueDalhousie
@rolls Why in the world would you want to do that? It's just strictly downside. It's not even necessarily correct to always do a null check before getting the value; forcing it is often enough going to be wrong.Chamfer
It was a hackish way to enforce that user checks if something is null before using it. Though you can just use resharper to inform you. I would never do it but it seems like that was they wanted, enforced null checking for anything that can be null.Dalhousie
"Why [...] would you want to do that?" -- to be sure the check is done in the mainline non-null case during development, rather than finding out in the field that there's no check for null. It's not as good as enforcing the check at compile time, but it's the next best thing. "It's not even necessarily correct to always do a null check ..." -- of course it is, which is why a proper Option type enforces that check at compile time. It's apparent that you don't understand the whole concept of Optional and monads: en.wikipedia.org/wiki/Option_type#The_option_monadExhaustion
@JimBalter The whole point is that the Optional class always does do the null check before providing the underlying value, so there's no reason to force the caller to check it again. If the caller wants an exception to be thrown if they dereference a null value, which is the correct behavior in many situations, then there's nothing wrong with that. It's not like they can get a garbage value without realizing it's not a valid value. Did you not even look at the code?Chamfer
Sorry, as I said, you don't understand the concept. Over and out.Exhaustion
@JimBalter So care to explain why doing the check a second time is in any way useful? Why do you think that checking the value once isn't enough? Do you expect it to be different the second time you check the same value?Chamfer
@Chamfer It would be useful if it had methods like forEach and map in it.Abloom
@Abloom That behavior is now backed into the language in the form of the null propagation operators, but sure, if you're going to write your own optional type, you could write such a method.Chamfer
the useful part of Optional in Java comes from the methods like filter, map, etc. which automatically handle if there is no value inside and can return an Empty Optional instead of throwing an exception. at the end of a chain of .filter().map()... you can use isPresent, isEmpty, orElse, etcGestation
I tend to agree that without map, filter, etc. it doesn't provide a ton of value simply because you're trading purely functional pipelines with operators to short circuit the code when it's a None.Humor
B
13

There is better implementation of option type in C#. You can find this implemenation in Tactical design patterns in .NET by Zoran Horvat at pluralsight.com. It includes an explanation why and how to use it. The basic idea is to implement option class as implementation of IEnumerable<> interface.

public class Option<T> : IEnumerable<T>
{
    private readonly T[] data;

    private Option(T[] data)
    {
        this.data = data;
    }

    public static Option<T> Create(T element)
    {
        return new Option<T>(new[] { element });
    }

    public static Option<T> CreateEmpty()
    {
        return new Option<T>(new T[0]);
    }

    public IEnumerator<T> GetEnumerator()
    {
        return ((IEnumerable<T>) this.data).GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return this.GetEnumerator();
    }
}
Bloodthirsty answered 26/3, 2016 at 11:33 Comment(1)
He wrote his own answer here.Sucking
H
4

In the project "C# functional language extensions" https://github.com/louthy/language-ext exists the Option object of F# among others functional patters

Housen answered 25/9, 2016 at 18:27 Comment(1)
as of the end of 2017 I would highly recommend adding this to your project. It includes a lot of other functional goodies but even if you don't understand them the option type is great.Abutilon
R
4

Instead of writing your own class, you could use Microsoft.FSharp.Core.FSharpOption<T> from the FSharpCore.dll assembly. Unfortunately, the F# types are a bit clumsy when used in C#.

//Create
var none = FSharpOption<string>.None;
var some1 = FSharpOption<string>.Some("some1");
var some2 = new FSharpOption<string>("some2");

//Does it have value?
var isNone1 = FSharpOption<string>.get_IsNone(none);
var isNone2 = OptionModule.IsNone(none);
var isNone3 = FSharpOption<string>.GetTag(none) == FSharpOption<string>.Tags.None;

var isSome1 = FSharpOption<string>.get_IsSome(some1);
var isSome2 = OptionModule.IsSome(some1);
var isSome3 = FSharpOption<string>.GetTag(some2) == FSharpOption<string>.Tags.Some;

//Access value
var value1 = some1.Value; //NullReferenceException when None
var value2 = OptionModule.GetValue(some1); //ArgumentException when None
Rhigolene answered 5/12, 2016 at 10:58 Comment(0)
J
3

There's nothing built-in, but you can define your own. Note that an Option<T> implementation doesn't make sense without defining the map/bind operators.

public struct Option<T>
{
    private bool hasValue;
    private T value;

    public Option(T value)
    {
        if (value == null) throw new ArgumentNullException("value");
        this.hasValue = true;
        this.value = value;
    }

    public Option<TOut> Select<TOut>(Func<T, TOut> selector)
    {
        return this.hasValue ? new Option<TOut>(selector(this.value)) : new Option<TOut>();
    }

    public Option<TOut> SelectMany<TOut>(Func<T, Option<TOut>> bind)
    {
        return this.hasValue ? bind(this.value) : new Option<TOut>();
    }

    public bool HasValue
    {
        get { return this.hasValue; }
    }

    public T GetOr(T @default)
    {
        return this.hasValue ? this.value : @default;
    }
}
Japan answered 24/4, 2013 at 18:32 Comment(1)
Not a fan of the Java-esq implementation but +1 for mentioning the operator requirements.Humor
A
3

Perhaps this is closer to the F# Option type

public struct Option<T>
{
    private T value;
    private readonly bool hasValue;

    public bool IsSome => hasValue;

    public bool IsNone => !hasValue;

    public T Value
    {
        get
        {
            if (!hasValue) throw new NullReferenceException();
            return value;
        }
    }

    public static Option<T> None => new Option<T>();

    public static Option<T> Some(T value) => new Option<T>(value);

    private Option(T value)
    {
        this.value = value;
        hasValue = true;
    }

    public TResult Match<TResult>(Func<T, TResult> someFunc, Func<TResult> noneFunc) =>
        hasValue ? someFunc(value) : noneFunc();

    public override bool Equals(object obj)
    {
        if (obj is Option<T>)
        {
            var opt = (Option<T>)obj;
            return hasValue ? opt.IsSome && opt.Value.Equals(value) : opt.IsNone;
        }
        return false;
    }

    public override int GetHashCode() =>
        hasValue ? value.GetHashCode() : 0;
}
Angeles answered 9/12, 2016 at 16:16 Comment(0)
A
2

I decided to implement some kind of Optional<> Java class prototype some time ago using one of the last C# version.

Here it is:

public sealed class Optional<T>
{
    private static readonly Optional<T> EMPTY = new Optional<T>();
    private readonly T value;

    private Optional() => value = default;
    private Optional(T arg) => value = arg.RequireNonNull("Value should be presented");

    public static Optional<T> Empty() => EMPTY;
    public static Optional<T> Of(T arg) => new Optional<T>(arg);
    public static Optional<T> OfNullable(T arg) => arg != null ? Of(arg) : Empty();
    public static Optional<T> OfNullable(Func<T> outputArg) => outputArg != null ? Of(outputArg()) : Empty();

    public bool HasValue => value != null;

    public void ForValuePresented(Action<T> action) => action.RequireNonNull()(value);

    public IOption<T> Where(Predicate<T> predicate) => HasValue 
        ? predicate.RequireNonNull()(value) ? this : Empty() : this;

    public IOption<TOut> Select<TOut>(Func<T, TOut> select) => HasValue 
        ? Optional<TOut>.OfNullable(select.RequireNonNull()(value)) 
        : Optional<TOut>.Empty();

    public IOption<IOption<TOut>> SelectMany<TOut>(Func<T, IOption<TOut>> select) => HasValue 
        ? Optional<IOption<TOut>>.OfNullable(select.RequireNonNull()(value)) 
        : Optional<IOption<TOut>>.Empty();

    public T Get() => value;
    public T GetCustomized(Func<T, T> getCustomized) => getCustomized.RequireNonNull()(value);
    public U GetCustomized<U>(Func<T, U> getCustomized) => getCustomized.RequireNonNull()(value);

    public T OrElse(T other) => HasValue ? value : other;
    public T OrElseGet(Func<T> getOther) => HasValue ? value : getOther();
    public T OrElseThrow<E>(Func<E> exceptionSupplier) where E : Exception => HasValue ? value : throw exceptionSupplier();

    public static explicit operator T(Optional<T> optional) => OfNullable((T) optional).Get();
    public static implicit operator Optional<T>(T optional) => OfNullable(optional);

    public override bool Equals(object obj)
    {
        if (obj is Optional<T>) return true;
        if (!(obj is Optional<T>)) return false;
        return Equals(value, (obj as Optional<T>).value);
    }

    public override int GetHashCode() => base.GetHashCode();
    public override string ToString() => HasValue ? $"Optional has <{value}>" : $"Optional has no any value: <{value}>";
}
Accelerate answered 7/5, 2018 at 8:25 Comment(0)
C
2

Use T? nullable reference instead of Option<T>

Since C#8 you should deprecate custom Option<T>-implementations. The null dilemma is now completely resolved.

T? is a complete substitution for Option<T>

C# has the following features for handling null:

  1. Null coalescing operator
  2. Null conditional operator
  3. Non nullable & nullable reference types (since C#8)
  4. Configurable compile errors/warnings

Keep in mind that

Option<Car> optional = Option<Car>.Create(myCar);
string color = optional
  .Select(car => car.Color.Name)
  .DefaultIfEmpty("<no car>")
  .Single();  // you can call First(), too

is the same as

string color = myCar?.Color.Name ?? "<no car>";

and additionally the string color is also a reference that can't be null.

Claus answered 19/2, 2020 at 15:23 Comment(13)
I supported my answer with strong and valid arguments and I am highly interested in the reasons for the downvotes.Claus
Option<T> is not the same thing as a Nullable-reference in C# 8.0 though - sometimes "success" can be indicated by a null-value (e.g. returning a String value that contains an error message vs. returning null on success).Englacial
@Englacial This is possible with string? and it is explicit that it may contain null.Claus
But that still requires the author of the String?-returning method to document that "null indicates success", whereas using a result-type means that documentation is unnecessary (I said Option<T> in my earlier comment but I meant to say "Result-type like SuccessOr<TError> or similar).Englacial
@Englacial Yeah, it is a different concept if you induce a special meaning with the class name. My answer applies to a general Option<T>.Claus
In your answer you talk about Option<T> vs T?, but that is kind of offtopic. You might be right and it might be a high quality comparison, but OP never asked about it. Apart from that: yea, people just downvoting without providing feedback is fruststrating. I hate it myself while being guilty of it tooNeall
@Neall It's not offtopic because T? is a complete substitution for Option<T>. And it is a native solution embedded into the language with operators, compile errors/warnings etc.. That makes Option<T> class obsolete in my opinion. I think it is therefore a very important answer to this questionClaus
Not a downvoter but I disagree that it's a complete substitution. But I do agree 8 added some syntatic sugar that makes them more friendly to deal with. But aren't you then pushing the responsibility on the consumer? Wouldn't that imperative code need to be used every time vs building a pipeline? And aren't you just dealing with a basic T?? So given some t, you can't do t.Map(...)...; as you would with an Option and fp? I agree with another answer that says "Note that an Option<T> implementation doesn't make sense without defining the map/bind operators"; T? doesn't have them.Humor
The problem with T? instead of Option<T> is that it assumes that null is not a valid return value, but a string can be null and an optional can have null ass a successful result so T? is more limited and not a full replacement.Volvulus
@DavidMårtensson I don't understand your argument. T? explicitely states that null is a valid value. A string can't be null with nullable refs. Only string? can be null.Claus
@Humor Map and Bind is just t?.. The only problem is that it is selecting IEnumerable<>? instead of a natural IEnumerable<>. However you also can end up with an Option<IEnumerable<> (see Lee's answer - SelectMany). An interesting aspect is the imperative nature of T?. But there are other mechanisms how code can become declarative, e.g. Func's or Prop-Getters. Option implementation may also be imperative (also see Lee's answer). Building pipepline works with both (see my sample)Claus
@FelixKeil Sure, but adding ? for any other value type has the meaning that it is expected to contain null values,. Option on the other hand makes a very clear difference between having a value and not having a value. And that very visible intent still makes Option a good type. And it allows you to start building code using option before switching to nullable ref. If you deprecate Option when nullable ref is activated you break compatibility and old code would break, or at least start giving warnings indicating you need to rewrite and that could harm acceptance of nullable refVolvulus
T? means something like T might be null. Optional<T> means this'll return a valid T or nothing. "valid" here means 'expected' or 'intended' in the happy path. It's a very subtle distinction I think, because everything you're saying @FelixKeil is true but there's a quality that's missing. Strange that I can't put my finger on it to explain though.Kickapoo
A
1

If you don't like baking your own solutions, I would use Language Ext. It is available on nuget. I have recently started using this library, and the safety from null references is amazing! I am not a expert with this library, but it can do what you are asking for, and much more.

Here is a taste of what can be done:

using System;
using LanguageExt;
using static LanguageExt.Prelude;

public class Demo
{
    public static Option<int> ToEvenOrNone(int i) =>
        i % 2 == 0
            ? i.Apply(Optional)
            : None;

    public static void PrintToDebug(Option<int> value) => 
        value
            .Some(Console.WriteLine)
            .None(() => Console.WriteLine("Value is not Even!"));

    public static void Test()
    {
        for (int i = 0; i < 10; i++)
        {
            PrintToDebug(ToEvenOrNone(i));
        }
    }
}

Here is the output:

0
Value is not Even!
2
Value is not Even!
4
Value is not Even!
6
Value is not Even!
8
Value is not Even!
Arad answered 12/6, 2020 at 10:50 Comment(0)
E
0

Is there a Nullable/Optional class in C#, that forces us to test if object exists before extracting and using it?

Nullables were created so that primitive types could be null. Their default value didn't have to be an actual value (Like int, without nullables it's default is 0, so is that a 0 means something 0 or a not set to anything 0?)

No there is nothing that you can do to force a programmer to check if an object is null. That's good though. Doing so would create an immense amount of overhead. If this was a language feature, how often would you force check? Would you require it when the variable is first assigned? What if the variable points to another object later? Would you force it to check before every method and property, and if it fails would you throw an exception? You get that now with a null reference exception. You would get very little benefit in forcing someone to do this beyond what you already have.

Ermines answered 24/4, 2013 at 18:22 Comment(4)
You wouldn't need to check - you would only provide a GetOr method which would require the user to provider a default value if there isn't one. You can easily define map\bind methods in C# to propagate missing values.Japan
I totally agree with Kevin. On the other hand if code aesthetics are concerned you can always use an extention method to chack null value. static bool isPresent(this object instance) { return null != instance; }Amboina
null is essentially the default value, saying "Hey there's nothing here" It tells the programmer "I didn't set this to anything." If it has a default value, then you always have to know what that value is for any variable, property etc. It'll always be a guessing game of, "is this a real value or a default value." Null solves that, because it's always the default.Ermines
null isn't a default value - it's no value, and its use is unsafe. That is what Option<T> is for - to make the lack of a value explicit. With reference types in C# you can never express that a reference should always point to an object.Japan
F
0

Learned a lot from Zoran Horvat's answer. Here is my code. optional can has a real value or an empty. On the consuming side, same code handle them all.

void Main()
{
    var myCar = new Car{ Color =  Color.Black, Make="Toyota"};

    Option<Car> optional = Option<Car>.Create(myCar);

    // optional is an Empty 50% of the time.
    if(new Random().NextDouble() > 0.5)
        optional = Option<Car>.CreateEmpty();



    string color = optional
    .Select(car => car.Color.Name)
    .DefaultIfEmpty("<no car>")
    .Single();
    Console.Write(color);
}

class Car {
    public Color Color { get; set; }
    public string Make { get; set;}
}

public class Option<T> : IEnumerable<T>
{
    private readonly T[] data;

    private Option(T[] data)
    {
        this.data = data;
    }

    public static Option<T> Create(T value)
    {
        return new Option<T>(new T[] { value });
    }

    public static Option<T> CreateEmpty()
    {
        return new Option<T>(new T[0]);
    }

    public IEnumerator<T> GetEnumerator()
    {
        return ((IEnumerable<T>)this.data).GetEnumerator();
    }

    System.Collections.IEnumerator
        System.Collections.IEnumerable.GetEnumerator()
    {
        return this.data.GetEnumerator();
    }
}
Footrest answered 4/4, 2019 at 21:10 Comment(1)
The problem with this or any implementation inherited from IEnumerable (IE) is that Option contains at most one value where an IE has 0 or more. In addition to that just being generally confusing from an IE POV, it couples a non-standard API to Option that makes it confusing as well. For example, the entire IE interface aside from a few (Select, Where, and SelectMany). But more importantly, it lacks a None which will clutter your pipelines . If going the IE route is the best for your case, I'd suggest using composition over inheritance and hiding all that extra baggage.Humor
D
0

https://github.com/mcintyre321/OneOf

I thought this oneOf class had a good re-creation of option type. It even includes a .switch/.match with pattern matching, and most importantly it works at runtime which is what you expect out of an Option pattern.

Disease answered 24/2, 2021 at 15:58 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.