DEV Community

Mark Clearwater
Mark Clearwater

Posted on • Originally published at blog.csmac.nz on

Looking back on C# 6: Elvis Operator

Looking back on C# 6: Elvis Operator

With C# 8 on our doorstep, I wanted to go through some of the C# 6 and 7 language features I have been using that you may have missed.

We start with the Elvis Operator from C# 6. The real name is Null Conditional Operator I believe, but I prefer the former.

It is always good to first look at what the simplest, common code scenario the feature addresses.

var widget = service.GetWidget(widgetId);

if(widget.IsInStock())
{
   service.PurchaseWidgets(widgetId, quantity);
}
Enter fullscreen mode Exit fullscreen mode

A simple scenario to purchase some widgets. Pretty standard code.

But what if GetWidget returns null when the id is incorrect? (Whether returning nulls is a good idea is another topic, so we won't go into that here.) We get a NullReferenceException when we try to check widget.IsInStock().

We can fix that:

var widget = service.GetWidget(widgetId);

if(widget != null && widget.IsInStock())
{
   service.PurchaseWidgets(widgetId, quantity);
}
Enter fullscreen mode Exit fullscreen mode

We add a null guard and everything works again as expected. The code becomes a bit more verbose for these very common use cases.

Instead, we can use the new(ish) symbol, the Null Conditional Operator or "Elvis Operator", which looks like ?. when used, which at a squint looks a bit like two eyes (..) and a coif of hair resembling the look made famous by Elvis Presley. (Technically it's just the ? but it usually ends up with a . following it.)

This symbol is a Null Check. It means "if the evaluated expression on the left is null, don't evaluate the right-hand side, and evaluate to a null value if required."

Some example usages:

interface ITestInterface {
    int ANumberProperty { get; }
    void DoSomeWork();
    string GetSomeData();
    long GetALong();
}

// getValue returns an `ITestInterface`
ITestInterface aValue = getValue();

// Safely get the value from a Property when the object might be null
int? aNumber = aValue?.ANumberProperty;

// if aValue is not null, execute `.DoSomeWork()`
aValue?.DoSomeWork();

// if aValue is not null, execute `.GetSomeData()`
// at this point, data could be null, and as a string, could have been anyway
string data = aValue?.GetSomeData();

// if aValue is not null, execute `.GetALong()`
// at this point, the result is now nullable
// We should all try to get along...
long? aLong = aValue?.GetALong();
Enter fullscreen mode Exit fullscreen mode

For our above example, this means we can now write this:

if(widget?.IsInStock() == true)
{
   service.PurchaseWidgets(widgetId, quantity);
}
Enter fullscreen mode Exit fullscreen mode

This is functionally identical code to the working example above. Note that because if(...) expects a boolean expression (something that evaluates to either true or false) we need to add == true to the end. This is because widget?.IsInStock() evaluates to Nullable<bool>, or bool? (The original bool becomes bool? thanks to the Elvis Operator.) Personally, I think this is very readable code.

One other ideal scenario for this is Function Pointers. Specifically, the language feature of Delegates, Funcs and Actions. To make this work, you have to use the Invoke method, rather than execute them like functions. Examples of what I mean are below.

public event PropertyChangedEventHandler PropertyChanged;

private void OnPropertyChanged(string propertyName)
{
    // This is a pretty traditional EventHandler scenario implementation
    var handler = PropertyChanged;
    if (handler != null)
        handler.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
Enter fullscreen mode Exit fullscreen mode

If this code doesn't look familiar, skip to the next paragraph. Otherwise, I'll go on. Events and EventHandler code can have problems if no-one has actually subscribed to the event, the execution could fail. But that's where we update to use the Elvis Operator, and throw away a bunch of code. You don't even have to create a shadow-copy variable to avoid multithread update race conditions, because the code generated by the compiler does that for you!

private void OnPropertyChanged(string propertyName)
{
    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
Enter fullscreen mode Exit fullscreen mode

For Funcs and Actions, you can safeguard against null values using the Elvis Operator as well.

public void DoWork(Action<string> logStuff)
{
    // Work happens here

    // This throws when `logStuff` is null
    // logStuff("Finished Doing Work."); 

    // This will work, only executing if logStuff is not null
    logStuff?.Invoke("Finished Doing Work.");
}

public Stuff GetStuff(Func<string> getMoreStuff)
{
    // Work happens here

    // This also throws when `getMoreStuff` is null
    // var moreStuff = getMoreStuff();

    // moreStuff will be null if getMoreStuff is null, OR if getMoreStuff returns null
    // You still might do a null check if you care about the difference
    var moreStuff = getMoreStuff?.Invoke();

    // We could also use the null coalescing operator (`??`) as well to fallback in both cases:
    var another = getMoreStuff?.Invoke() ?? "FallBack Value";

    return new Stuff
    {
        Thing1 = 3.4,
        Thing2 = 3.3,
        MoreStuff = moreStuff
    };
}
Enter fullscreen mode Exit fullscreen mode

While I often hear people talking about "clever code" as an antipattern, and I totally agree with that statement, I find the argument for any particular feature or technique being "clever" is very time sensitive. As a language feature reaches a large enough common usage status, it is no longer considered "clever". (Historical examples of this might be foreach Extension methods, lambdas, even LINQ statements and async compared to two years ago.)

That applies here as well. It is time to embrace the Elvis Operator in your code.

Top comments (0)