43
votes

Is it possible to overload the null-coalescing operator for a class in C#?

Say for example I want to return a default value if an instance is null and return the instance if it's not. The code would look like something like this:

   return instance ?? new MyClass("Default");  

But what if I would like to use the null-coalescing operator to also check if the MyClass.MyValue is set?

6
Patrik, a use: I wanted to overload string's null-coalescing operator to consider empty as null. Whether that is sensible or whatever, that is a different question. - ANeves

6 Answers

30
votes

Good question! It's not listed one way or another in the list of overloadable and non-overloadable operators and nothing's mentioned on the operator's page.

So I tried the following:

public class TestClass
{
    public static TestClass operator ??(TestClass  test1, TestClass test2)
    {
        return test1;
    }
}

and I get the error "Overloadable binary operator expected". So I'd say the answer is, as of .NET 3.5, a no.

15
votes

According to the ECMA-334 standard, it is not possible to overload the ?? operator.

Similarly, you cannot overload the following operators:

  • =
  • &&
  • ||
  • ?:
  • ?.
  • checked
  • unchecked
  • new
  • typeof
  • as
  • is
7
votes

Simple answer: No

C# design principles do not allow operator overloading that change semantics of the language. Therefore complex operators such as compound assignment, ternary operator and ... can not be overloaded.

3
votes

This is rumored to be part of the next version of C#. From http://damieng.com/blog/2013/12/09/probable-c-6-0-features-illustrated

7. Monadic null checking

Removes the need to check for nulls before accessing properties or methods. Known as the Safe Navigation Operator in Groovy).

Before

if (points != null) {
    var next = points.FirstOrDefault();
    if (next != null && next.X != null) return next.X;
}   
return -1;

After

var bestValue = points?.FirstOrDefault()?.X ?? -1;
0
votes

I was trying to accomplish this with a struct I wrote that was very similar Nullable<T>. With Nullable<T> you can do something like

Nullable<Guid> id1 = null;
Guid id2 = id1 ?? Guid.NewGuid();

It has no problem implicitly converting id1 from Nullable<Guid> to a Guid despite the fact that Nullable<T> only defines an explicit conversion to type T. Doing the same thing with my own type, it gives an error

Operator '??' cannot be applied to operands of type 'MyType' and 'Guid'

So I think there's some magic built into the compiler to make a special exception for Nullable<T>. So as an alternative...

tl;dr

We can't override the ?? operator, but if you want the coalesce operator to evaluate an underlying value rather than the class (or struct in my case) itself, you could just use a method resulting in very few extra keystrokes required. With my case above it looks something like this:

public struct MyType<T>
{
    private bool _hasValue;
    internal T _value;

    public MyType(T value)
    {
        this._value = value;
        this._hasValue = true;
    }

    public T Or(T altValue)
    {
        if (this._hasValue)
            return this._value;
        else
            return altValue;
    }
}

Usage:

MyType<Guid> id1 = null;
Guid id2 = id1.Or(Guid.Empty);

This works well since it's a struct and id1 itself can't actually be null. For a class, an extension method could handle if the instance is null as long as the value you're trying to check is exposed:

public class MyClass
{
    public MyClass(string myValue)
    {
        MyValue = myValue;
    }

    public string MyValue { get; set; }
}

public static class MyClassExtensions
{ 
    public static string Or(this MyClass myClass, string altVal)
    {
        if (myClass != null && myClass.MyValue != null)
            return myClass.MyValue;
        else
            return altVal;
    }
}

Usage:

MyClass mc1 = new MyClass(null);
string requiredVal = mc1.Or("default"); //Instead of mc1 ?? "default";
-3
votes

If anyone is here looking for a solution, the closest example would be to do this

return instance.MyValue != null ? instance : new MyClass("Default");