Unique ways to use the Null Coalescing operator

2019-01-08 04:14发布

问题:

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)

回答1:

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

string anybody = parm1 ?? localDefault ?? globalDefault;

vs.

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

It also works well if 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"];


回答2:

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

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

Readable? Decide for yourself.



回答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.



回答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.



回答5:

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

So there are scenarios where you shouldn't use ternary, 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 equals to 2, and if you use:

var b = a.Prop ?? 0

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



回答6:

The biggest advantage that I find to the ?? operator is that you can easily convert nullable value types to non-nullable types:

int? test = null;
var result = test ?? 0; // result is int, not int?

I frequently use this in Linq queries:

Dictionary<int, int?> PurchaseQuantities;
// PurchaseQuantities populated via ASP .NET MVC form.
var totalPurchased = PurchaseQuantities.Sum(kvp => kvp.Value ?? 0);
// totalPurchased is int, not int?


回答7:

I've used ?? in my implementation of IDataErrorInfo:

public string Error
{
    get
    {
        return this["Name"] ?? this["Address"] ?? this["Phone"];
    }
}

public string this[string columnName]
{
    get { ... }
}

If any individual property is in an "error" state I get that error, otherwise I get null. Works really well.



回答8:

You can use the null coalescing operator to make it a bit cleaner to handle the case where an optional parameter is not set:

public void Method(Arg arg = null)
{
    arg = arg ?? Arg.Default;
    ...


回答9:

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

Actually, my experience is that all too few people are familiar with the ternary operator (or more correctly, the conditional operator; ?: is "ternary" in the same sense that || is binary or + is either unary or binary; it does however happen to be the only ternary operator in many languages), so at least in that limited sample, your statement fails right there.

Also, as mentioned before, there is one major situation when the null coalescing operator is very useful, and that is whenever the expression to be evaluated has any side effects at all. In that case, you cannot use the conditional operator without either (a) introducing a temporary variable, or (b) changing the actual logic of the application. (b) is clearly not appropriate in any circumstances, and while it's a personal preference, I don't like cluttering up the declaration scope with lots of extraneous, even if short-lived, variables, so (a) is out too in that particular scenario.

Of course, if you need to do multiple checks on the result, the conditional operator, or a set of if blocks, are probably the tool for the job. But for simple "if this is null, use that, otherwise use it", the null coalescing operator ?? is perfect.



回答10:

Only problem is the null-coalesce operator doesn't detect empty strings.


i.e.

string result1 = string.empty ?? "dead code!";

string result2 = null ?? "coalesced!";

OUTPUT:

result1 = ""

result2 = coalesced!

I'm currently looking into overriding the ?? operator to work around this. It sure would be handy to have this built into the Framework.

Thoughts?



回答11:

I like to use the null coalesce operator to lazy load certain properties.

A very simple (and contrived) example just to illustrate my point:

public class StackOverflow
{
    private IEnumerable<string> _definitions;
    public IEnumerable<string> Definitions
    {
        get
        {
            return _definitions ?? (
                _definitions = new List<string>
                {
                    "definition 1",
                    "definition 2",
                    "definition 3"
                }
            );
        }
    } 
}


回答12:

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

You should use what best expresses your intent. Since there is a null coalesce operator, use it.

On the other hand, since it's so specialized, I don't think it has other uses. I would have preferred an appropriate overload of the || operator, as other languages do. This would be more parsimonious in the language design. But well …



回答13:

Cool! Count me as someone who didn't know about the null coalescing operator -- that's pretty nifty stuff.

I find it much easier to read than the ternary operator.

The first place that comes to mind where I might use it is to keep all of my default parameters in a single place.

public void someMethod( object parm2, ArrayList parm3 )
{ 
  someMethod( null, parm2, parm3 );
}
public void someMethod( string parm1, ArrayList parm3 )
{
  someMethod( parm1, null, parm3 );
}
public void someMethod( string parm1, object parm2, )
{
  someMethod( parm1, parm2, null );
}
public void someMethod( string parm1 )
{
  someMethod( parm1, null, null );
}
public void someMethod( object parm2 )
{
  someMethod( null, parm2, null );
}
public void someMethod( ArrayList parm3 )
{
  someMethod( null, null, parm3 );
}
public void someMethod( string parm1, object parm2, ArrayList parm3 )
{
  // Set your default parameters here rather than scattered through the above function overloads
  parm1 = parm1 ?? "Default User Name";
  parm2 = parm2 ?? GetCurrentUserObj();
  parm3 = parm3 ?? DefaultCustomerList;

  // Do the rest of the stuff here
}


回答14:

One thing I've been doing a lot lately is using null coalescing for backups to as. For example:

object boxed = 4;
int i = (boxed as int?) ?? 99;

Console.WriteLine(i); // Prints 4

It's also useful for backing up long chains of ?. that could each fail

int result = MyObj?.Prop?.Foo?.Val ?? 4;
string other = (MyObj?.Prop?.Foo?.Name as string)?.ToLower() ?? "not there";


回答15:

Bit of a weird use case, but I had a method where an IDisposable object is potentially passed as an arg (and therefore disposed by parent), but could also be null (and so should be created and disposed in local method)

To use it, the code either looked like

Channel channel;
Authentication authentication;

if (entities == null)
{
    using (entities = Entities.GetEntities())
    {
        channel = entities.GetChannelById(googleShoppingChannelCredential.ChannelId);
        [...]
    }
}
else
{
    channel = entities.GetChannelById(googleShoppingChannelCredential.ChannelId);
    [...]
}

But with a null coalesce becomes much neater

using (entities ?? Entities.GetEntities())
{
    channel = entities.GetChannelById(googleShoppingChannelCredential.ChannelId);
    [...]
}


回答16:

Null-Coalescing Operator

This operator check the variable, if null, return the value next to the “??” operator else give the value saved in the variable.

Eg:-1

var name=”ATHUL”;
var result =name ?? “The name is null”
Console.WriteLine(result);

o/p : ATHUL

Eg:-2

var name=null;
var result =name ?? “The name is null”
Console.WriteLine(result);

o/p : The name is null