Roslyn: Proposal: null-coalescing assignment operator ??=

Created on 12 Sep 2016  路  15Comments  路  Source: dotnet/roslyn

Proposal
I'd like to propose an idea for next C#, with name: null-coalescing assignment operator
We can combine the ?? and = operators (??=) like, += and -= ...

Sample:
myObject.MyStringList ??= new List<string>();
Assign if the left is null...
In C# 6 we had:
myObject.MyStringList = myObject.MyStringList ?? new List<string>();

Another sample:
(myObject.MyStringList ??= new List<string>()).Add("something");

This is very simple, but useful...
Thanks

2 - Ready Area-Language Design Feature Request

Most helpful comment

@HaloFour makes me eat my words.

All 15 comments

Dupe of #205, #3366, #5163, #10249, #10556, #11394 and #12606.

Per @gafter:

The benefits of this proposal do not overcome the drawbacks in terms of language complexity. It is unlikely to ever have a high enough benefit-for-cost to make us prefer it over many other things we could do in this release or many releases into the future.

However, as mentioned at https://github.com/dotnet/roslyn/issues/205#issuecomment-215420655 there seems to be a decent amount of recurring interest in such an operator so there is a community prototype.

@HaloFour makes me eat my words.

This would probably make more sense along with #1276, so that ?. produces an lvalue.

x?.P = y;
if (x != null) x.P = y;

x?.P ??= y;
if (x != null) if (x.P == null) x.P = y;

@alrz I find ??= rather clear in its meaning while ?. lvalues look very confusing.

@orthoxerox they wouldn't be the same thing though the use cases are kinda similar.

I understand that, but the effect of x?.P?.Q = y; is not immediately obvious, it feels an annoying shorthand like while (*dst++=*src++);. Maybe I will change my mind when it becomes as idiomatic as the latter example.

@alrz

I'd be concerned about the subtleties of how that would be implemented. For example:

// is the method called even if x is null and the result simply discarded?
x?.P = SomeComplexCalculation();

// are there any assignments if x or y are null?
x?.P = y?.P = z;

Although I guess you could argue that assignment to a property isn't really any different from invoking a method:

x?.P = y;
// same as:
x?.SetP(y);
// same as:
if (x != null) { x.SetP(y); }

@HaloFour Beyond the simple case p ?= F() which can be translated to p ?? (p = F()) in an expression context, those concerns are also associated with the ?= operator.

x ?= y ?= F(); // as a statement
var r = x ?= y ?= F(); // as an expression

How these should be translated? I do prefer lazy evaluation of the right-hand-side, but it would be weird if y doesn't get initialized if x wasn't null!

PS: I like ?= operator better as we have |= which behaves like an = and || for booleans.

This promotes usage of mutability and null.

@dsaf,

You summarised my dislike of this proposal far more succinctly than I was going to. :smile:

This promotes usage of mutability and null.

I disagree. You can't remove them either and some operations essentially need null and/or mutability, e.g. lazy initialization or caching. And since C# is not a pure functional language these patterns are pretty common (because you don't have the m-word to handle state). I'd argue that there should be some mechanism to _manage_ mutability and null instead of discouraging them, namely, #5032 and #7626.

some operations essentially need null and mutability, e.g. lazy initialization

It _is_ actually possible to have your proverbial cake and eat it too, by utilizing Lazy<T>. The people over at Microsoft have already worked hard on making that class mutable once _and_ thread-safe, so you don't have to.

Yes, it's a little more typing if you want to use it everywhere right now. Yes, there have been suggestions to add eg. a lazy keyword for auto-properties to make the compiler emit a Lazy<T> backing field (and @alrz himself even suggests this could be covered by Replace/Original+a marker Attribute, though his example uses ?? instead, which may or may not be thread-safe).

By "lazy initialization" I meant any initialization that occurs after construction (you don't always have access to the context from which the value is initialized), but sure, you could do it with a simple null check. More to the point, this operator is just a syntactical sugar which targets readability and conciseness. Anyways, I think the two proposals (#13737 and #1276) are complimentary and are more helpful together.

I strongly dislike Lazy<T> and never want to see it integrated as the only option for a lazy keyword.
It's definitely not for every situation. If your class is single-threaded by design, as all are unless you're doing something special, ??= is optimal.
See https://twitter.com/nick_craver/status/722040083944235008 too. 馃槅

This is now being tracked at https://github.com/dotnet/csharplang/issues/34

Was this page helpful?
0 / 5 - 0 ratings