Unique ways to use the null coalescing operator

69,977

Solution 1

Well, first of all, it's much easier to chain than the standard ternary operator:

string anybody = parm1 ?? localDefault ?? globalDefault;

vs.

string anyboby = (parm1 != null) ? parm1
               : ((localDefault != null) ? localDefault
               : globalDefault);

It also works well if a null-possible object isn't a variable:

string anybody = Parameters["Name"]
              ?? Settings["Name"]
              ?? GlobalSetting["Name"];

vs.

string anybody = (Parameters["Name"] != null ? Parameters["Name"]
                 : (Settings["Name"] != null) ? Settings["Name"]
                 :  GlobalSetting["Name"];

Solution 2

I've used it as a lazy load one-liner:

public MyClass LazyProp
{
    get { return lazyField ?? (lazyField = new MyClass()); }
}

Readable? Decide for yourself.

Solution 3

I've found it useful in two "slightly odd" ways:

  • As an alternative for having an out parameter when writing TryParse routines (i.e. return the null value if parsing fails)
  • As a "don't know" representation for comparisons

The latter needs a little bit more information. Typically when you create a comparison with multiple elements, you need to see whether the first part of the comparison (e.g. age) gives a definitive answer, then the next part (e.g. name) only if the first part didn't help. Using the null coalescing operator means you can write pretty simple comparisons (whether for ordering or equality). For example, using a couple of helper classes in MiscUtil:

public int Compare(Person p1, Person p2)
{
    return PartialComparer.Compare(p1.Age, p2.Age)
        ?? PartialComparer.Compare(p1.Name, p2.Name)
        ?? PartialComparer.Compare(p1.Salary, p2.Salary)
        ?? 0;
}

Admittedly I now have ProjectionComparer in MiscUtil, along with some extensions, which make this kind of thing even easier - but it's still neat.

The same can be done for checking for reference equality (or nullity) at the start of implementing Equals.

Solution 4

Another advantage is that the ternary operator requires a double evaluation or a temporary variable.

Consider this, for instance:

string result = MyMethod() ?? "default value";

while with the ternary operator you are left with either:

string result = (MyMethod () != null ? MyMethod () : "default value");

which calls MyMethod twice, or:

string methodResult = MyMethod ();
string result = (methodResult != null ? methodResult : "default value");

Either way, the null coalescing operator is cleaner and, I guess, more efficient.

Solution 5

Another thing to consider is that the coalesce operator doesn't call the get method of a property twice, as the ternary does.

So there are scenarios where you shouldn't use the ternary operator, for example:

public class A
{
    var count = 0;
    private int? _prop = null;
    public int? Prop
    {
        get 
        {
            ++count;
            return _prop
        }
        set
        {
            _prop = value;
        }
    }
}

If you use:

var a = new A();
var b = a.Prop == null ? 0 : a.Prop;

the getter will be called twice and the count variable will be equal to 2, and if you use:

var b = a.Prop ?? 0

the count variable will be equal to 1, as it should.

Share:
69,977

Related videos on Youtube

Armstrongest
Author by

Armstrongest

Front end work, Back end work. Cool kids call it the full stack. Ever curious developer of fine web stuff on the internets. "You can't work on the web and not be strive for polyglotism."

Updated on November 14, 2020

Comments

  • Armstrongest
    Armstrongest over 3 years

    I know the standard way of using the null coalescing operator in C# is to set default values.

    string nobody = null;
    string somebody = "Bob Saget";
    string anybody = "";
    
    anybody = nobody   ?? "Mr. T"; // Returns Mr. T
    anybody = somebody ?? "Mr. T"; // Returns "Bob Saget"
    

    But what else can ?? be used for? It doesn't seem as useful as the ternary operator, apart from being more concise and easier to read than:

    nobody = null;
    anybody = nobody == null ? "Bob Saget" : nobody; // Returns Bob Saget
    

    So given that fewer even know about null coalescing operator...

    • Have you used ?? for something else?

    • Is ?? necessary, or should you just use the ternary operator (that most are familiar with)

  • Godeke
    Godeke over 15 years
    Hmmm, you found a counterexample to "why would someone want to use it as an obfuscated IF"... that is actually very readable to me.
  • chakrit
    chakrit over 15 years
    The chaining is a big plus for the operator, removes a bunch of redundant IFs
  • Armstrongest
    Armstrongest over 15 years
    Interesting. You're using "this" as a property. I've never done that.
  • Matt Hamilton
    Matt Hamilton over 15 years
    Yeah, it's part of how IDataErrorInfo works. Generally that syntax is only useful on collection classes.
  • Armstrongest
    Armstrongest about 15 years
    You can do this with Extension methods, but I agree, it would be a nice addition to the code and very useful in a web context.
  • Max Galkin
    Max Galkin almost 15 years
    Yeah, this is a frequent scenario... there is even a special method String.IsNullOrEmpty(string)...
  • Beweelam
    Beweelam almost 15 years
    I like what you did with the PartialComparer, but was looking for cases where I need to keep the evaluated expression variables. I am not versed in lambdas and extensions, so could you see if the following adheres to a similar pattern (i.e. does it work)? stackoverflow.com/questions/1234263/#1241780
  • Chris Marisic
    Chris Marisic about 14 years
    This my primary usage of the Null Coalescing.
  • Justin K
    Justin K almost 14 years
    I might be missing something (I mostly use Java), but isn't there a race condition there?
  • Jeffrey L Whitledge
    Jeffrey L Whitledge almost 14 years
    @Justin K - There is only a race condition if multiple threads are accessing the LazyProp property of the same object. It's easily fixable with a lock, if thread safty of each instance is required. Clearly in this example, it is not required.
  • Justin K
    Justin K almost 14 years
    @Jeffrey: If it were clear, I wouldn't have asked the question. :) When I saw that example, I immediately thought of a singleton member, and since I happen to do most of my coding in a multithreaded environment... But, yeah, if we assume the code is correct, anything extra is unnecessary.
  • user
    user over 13 years
    +1. This is one big reason why I like the null coalescing operator. It's particularly useful when calling MyMethod() has any sort of side effects.
  • JAB
    JAB almost 13 years
    "the null-coalesce operator doesn't detect empty strings." Well it is the null -coalescing operator, not the nullOrEmpty -coalescing operator. And personally, I despise mixing null and empty values in languages that distinguish between the two, which makes interfacing with things that don't quite annoying. And I'm a bit obsessive-compulsive, so it annoys me when languages/implementations don't distinguish between the two anyway, even if I understand the reasoning why (like in [most implementations of?] SQL).
  • Kit
    Kit almost 12 years
    ?? can't be overloaded: msdn.microsoft.com/en-us/library/8edha89s(v=vs.100).aspx -- that would be a great one to have overloadable though. I use a combination: s1.Nullify() ?? s2.Nullify() where string Nullify(this s) returns a null in cases when the string is empty.
  • TinyTimZamboni
    TinyTimZamboni over 11 years
    If MyMethod() does not have any effects outside of returning a value, the compiler knows not to call it twice, so you really don't have to worry about efficiency here in the majority of cases.
  • xdhmoore
    xdhmoore about 11 years
    It is also keeps things more readable IMHO when MyMethod() is a chained sequence of dotted objects. Ex: myObject.getThing().getSecondThing().getThirdThing()
  • Niall Connaughton
    Niall Connaughton almost 11 years
    It doesn't have to be a Singleton to have a race condition. Just a shared instance of the class that contains LazyProp, and multiple threads that access LazyProp. Lazy<T> is a better way to do this kind of thing, and is threadsafe by default (you can elect to modift the threadsafety of Lazy<T>).
  • causa prima
    causa prima over 10 years
    The only problem? I just found myself wanting ??= and found this thread while seeing if there was a way to do it. (Situation: The first pass loaded the exception cases, now I want to go back through and load default values into anything that didn't already get loaded.)
  • CompuChip
    CompuChip over 10 years
    I may be a bit late here, but that second example will throw if kvp == null. And actually Nullable<T> has a GetValueOrDefault method that I normally use.
  • Ryan
    Ryan over 10 years
    KeyValuePair is a value type in the .NET framework, so accessing any of its properties would never throw a null reference exception. msdn.microsoft.com/en-us/library/5tbh8a42(v=vs.110).aspx
  • Andrew
    Andrew almost 9 years
    You are storing error messages in this["Name"], this["Address"], etc??
  • Kuba Wyrostek
    Kuba Wyrostek over 8 years
    This deserves more upvotes. I've read sooo many times that ?? is equivalent to ?:.
  • Kuba Wyrostek
    Kuba Wyrostek over 8 years
    @TinyTimZamboni, do you have a reference for this behavior of compiler?
  • TinyTimZamboni
    TinyTimZamboni over 8 years
    @KubaWyrostek I don't have knowledge of the specific workings of the C# compiler, but I have some experience with static compiler theory with llvm. There's a number of approaches a compiler can take to optimize a call like this. Global Value Numbering will notice that the two calls to MyMethod are identical in this context, assuming that MyMethod is a Pure function. Another option is Automatic Memoization or just having the function close in cache. On the other hand: en.wikipedia.org/wiki/Global_value_numbering
  • Kuba Wyrostek
    Kuba Wyrostek over 8 years
    MSDN on C# states that both condition and winning value are evaluated so I was wondering if C# specification follows this statement. To me this is not just a question of optimization. Two calls to MyMethod() are not thread-safe: returned value can differ between calls although the method has no side effects itself.
  • David A. Gray
    David A. Gray about 7 years
    I just used it today to replace a simple IF block that I wrote before I knew about either ternary or null coalescing operator. The true and false branches of the original IF statement called the same method, replacing one of its arguments with a different value if a certain input is NULL. With the null coalescing operator, it's one call. This is really powerful when you have a method that needs two or more such substitutions!
  • arichards
    arichards over 6 years
    Resharper will actually suggest this as a refactor for a "traditional" lazy load.
  • Linas
    Linas almost 6 years
    Valid point about a getter being called twice. But this example I would consider a bad design patter to have such misleadingly named getter to actually make changes to the object.
  • Steve Kidd
    Steve Kidd almost 6 years
    Very late comment from me - but pleased to see somebody cover that a ternary operator is an operator with three arguments (of which there is now more than one in C#).
  • Jesse de Wit
    Jesse de Wit about 5 years
    Wouldn't it be great if this line could be written as arg ?= Arg.Default?