??=
would have semantics analogous to the existing compound assignment operators like +=
, %=
, and <<=
. More precisely, x ??= y
would be equivalent to x = x ?? y
except that x
is evaluated only once. It would have clear, if not vast, utility. Hopefully, it would not be an unreasonably large effort.
It is perhaps debatable whether
object o = GetFirstChoice( foo, bar, someLongArgument )
?? GetSecondChoice( foo, bar, someLongArgument )
?? GetFallback();
looks better than
object o = GetFirstChoice( foo, bar, someLongArgument );
o ??= GetSecondChoice( foo, bar, someLongArgument );
o ??= GetFallback();
However, the latter seems easier to step through in the debugger.
Furthermore, a use case like
private static void ApplyNewSettings( SettingsSource newSource ) {
// Set each setting if and only if it is not already set to a non-null value.
// This could be e.g. applying defaults after deserializing user settings.
this.SomeSetting ??= newSource.SomeSetting;
// ...
}
is less repetitive with ??=
.
I agree the stepping through the debugger would be nice, but I don't think it would be clear whether o ??= GetFallback();
expands to:
o = o ?? GetFallback();
of
o = GetFalback() ?? o;
From your initial example it looks to be option 1, but the final use case would only work with option 2 (the semantics of ApplyNewSettings
with option 1 would be to provide defaults if it's not already assigned, it would not do anything if the settings are already defined)
@mirhagk I meant option 1 in both cases. ApplyNewSettings()
was meant to only set settings that were not already set (as opposed to set only settings present in the new settings). I added a clarifying comment. I apologize for the ambiguity.
I think it is a great idea. It might be worth to add a separate proposal for &&=
and ||=
as well.
I've always wanted this operator as well. When assigning default values for a variable that has already been declared (or a parameter passed in) this could save a lot of boilerplate and condense two lines of code into one succinct statement.
if(someLongAndAnnoyingToTypeVariableName == null)
someLongAndAnnoyingToTypeVariableName = new Foo();
vs.
someLongAndAnnoyingToTypeVariableName ??= new Foo();
@ashmind , I don't see a point in &&= or ||=
they are equivalent to &=
and |=
but they will not short circuit if the value is false or true respectively.
a &= SomeValue()
a &&=SomeValue()
@mburbea I'll create a separate item for those when I get a chance, to avoid confusing ??= discussion.
I think this would be a great semantic improvement. It's obvious that the left is being assigned only if it's null.
But the first exemple, at most, just needs a few parenthesis to work now.
It seems unlikely we'd ever do this.
@gafter Can you please elaborate?
Is it a question of effort, or is it rejected from the language design?
If it's the design, then it would be very useful good to discuss why, to avoid spending time on similar proposals (e.g. ||=).
If it's the effort, then can't it be "Up For Grabs" instead of "Won't Fix"?
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.
Given the amount of interest in it (5 linked items) I've decided to try making a prototype -- which is currently at https://github.com/ashmind/roslyn/tree/features/coalesce-assignment. That's my first attempt at a Roslyn thing, so I'm sure I'm doing everything wrong.
I've also added that branch to TryRoslyn:
http://tryroslyn.azurewebsites.net/#b:ashmind-features-coalesce-assignment/K4Zwlgdg5gBAygTxAFwKYFsDcAoADsAIwBswBjGUogQxBBgGEYBvbGNmFAJ0lgA8d2HZN2gwEA9q3b5iZGADcA9mAAmMALIAKAJTMpgtrxgBeGACJeZiQZgB6WzAAWyZLhAAue1DDJHhAHSkiui2KorIEKjItpyKIEQIELZgtMCoILYATAAMAKz6BggwAPzFpvwFAL7YlUA=
One open question from the prototype:
If we have x ??= y
, and x
is not null, should the assignment x = x
still happen?
The presence of that assignment would be observable if e.g. you have logic in property setter.
My feeling is it shouldn't, but would be good to know if I'm missing any edge cases.
I expect the answer to apply to any future implementations of &&=
and ||=
as well.
@ashmind Should this work for a conditional access? x?.y ??= z;
Related: #1276 -- "the ?. Operator never produces an lvalue"
btw, tryroslyn is amazing!
@ashmind I think it should not perform the assignment if x
is not null. The ??
operator is currently short circuiting.
http://csharppad.com/gist/680db2bb9216f2909ddb226f10a91c18
Nice work!
@ashmind I am pleased to see your prototype and delighted by its quality. The diff looks pretty clean and direct. The comments raise a couple good questions about the details of the correct behavior, particularly regarding type inference.
I agree that when x
is not null the assignment shouldn't happen, unless I am also missing some good reason.
@alrz
Thanks!
Should this work for a conditional access?
I think this must be consistent with standard assignment. If #1276 is ever implemented for properties then yes -- but only then.
@MgSam @judemelancon
Thanks for your feedback!
I think you are right -- there is no reason to perform assignment to the same value. That would mean I can't just naively lower to ??
, but I'll find a way to do some reuse.
dotnet/csharplang#34 is this feature.
Most helpful comment
Given the amount of interest in it (5 linked items) I've decided to try making a prototype -- which is currently at https://github.com/ashmind/roslyn/tree/features/coalesce-assignment. That's my first attempt at a Roslyn thing, so I'm sure I'm doing everything wrong.
I've also added that branch to TryRoslyn:
http://tryroslyn.azurewebsites.net/#b:ashmind-features-coalesce-assignment/K4Zwlgdg5gBAygTxAFwKYFsDcAoADsAIwBswBjGUogQxBBgGEYBvbGNmFAJ0lgA8d2HZN2gwEA9q3b5iZGADcA9mAAmMALIAKAJTMpgtrxgBeGACJeZiQZgB6WzAAWyZLhAAue1DDJHhAHSkiui2KorIEKjItpyKIEQIELZgtMCoILYATAAMAKz6BggwAPzFpvwFAL7YlUA=