Today we’ll make a new type that addresses some of the deficiencies in Nullable<T>. We’ll end up with a good tool for dealing with operations that may or may not produce a result or take a parameter, even in Burst-compiled code. Read on to see how it works!


Nullable<T> is built into C# via the syntax T?. It’s a struct containing a T value and a bool that indicates whether the value is usable or not. It overloads the term “null” so that it can be used with T, which normally can’t be null because it’s constrained to be a struct, a primitive, or an enum.

This means there’s no way to use Nullable<T> with a class, delegate, or any other reference type. We could use null with these types, but that wouldn’t catch accidental usage of the value like Nullable<T> does. NotNull is a another option, but it doesn’t allow for null at all.

To enforce that the T value is never used when it is “null”, the field is private and the property that accesses it checks the bool to make sure access is allowed. When a user of the Nullable<T> tries to access a “null” value, the property throws an exception. It does this in all build types, including production, which introduces some slowness.

Nullable<T> also attempts to behave like a T. It provides functions like Equals, GetHashCode, and ToString. Unfortunately, all of these call virtual functions of object which causes the T to be boxed to an object. This boxing creates garbage for the GC to later collect, possibly causing frame spikes.


Optional<T> will address these issues, and more, by making the following changes:

  • T may be any type, not just structs, primitives, and enums
  • Getting the T value only throws an exception when UNITY_ASSERTIONS is defined
  • Remove all garbage-creating functionality like Equals, GetHashCode, and
    <li>Allow setting and resetting the <code>T
  • Take a default parameter in the constructor
  • Add overloaded operators for bool, true, and
    Immplementing these changes is extremely straightforward, so let's just look at the full source code. It weighs in at just 73 SLOC plus a whole lot of comments.
    Now let's take a look at how we can use <code>Optional&lt;T&gt;
    to take advantage of some of the changes. Here’s a function that searches for a best target GameObject and some code to use that function:
    // Function clearly advertises that it might not find a target
    Optional<GameObject> FindBestTarget()
        Optional<GameObject> bestTarget = default; // Initially has no value
        int bestTargetScore = int.MinValue;
        foreach (GameObject target in Targets)
            int score = GetTargetScore(target);
            if (score > bestTargetScore)
                bestTarget.Value = target; // Now it has a value
                bestTargetScore = score;
        return bestTarget;
    // User code
    // Very clear that the returned value might not be usable
    // Much less clear when using 'null'
    // Uses 'operator bool' for terseness
    Optional<GameObject> bestTarget = FindBestTarget();
    if (bestTarget)

    It’s not hard to rewrite even some of the most basic types in C#. If we do, we can customize them to fit our needs at the cost of a little syntax sugar: T?. In the case of Optional&lt;T&gt;, we got benefits ranging from increased flexibility to less garbage creation to faster execution to improved usability. Hopefully you’ll find the type useful!