logoalt Hacker News

What's New in C# 14: Null-Conditional Assignments

117 pointsby ivankahllast Monday at 6:08 PM103 commentsview on HN

Comments

_ZeD_today at 7:49 AM

While I can understand the reason behind the behaviour I cannot find it intuitive.

If I say an asseignment I expect the value to be "evaluated".

I could have grasped the expression in all the null values would have been replaced with new instances, but then it would have been too much invasive and magic to work, so - again - I understand why the designers night have been force to settle for the actual behaviour...

But maybe then the half-solution is not worth it

maltalextoday at 4:19 AM

Cute, but is this actually needed? It's one more thing to remember, one more thing to know the subtleties of, and for what? To save writing a very readable and unambiguous line of code?

It feels like the C# designers have a hard time saying "no" to ideas coming their way. It's one of my biggest annoyances with this otherwise nice language. At this point, C# has over 120 keywords (incl. contextual ones) [0]. This is almost twice as much as Java (68) [1], and 5 times as much as Go (25) [2]. And for what? We're trading brevity for complexity.

[0]: https://learn.microsoft.com/en-us/dotnet/csharp/language-ref... keywords/

[1]: https://en.wikipedia.org/wiki/List_of_Java_keywords

[2]: https://go.dev/ref/spec#Keywords

show 2 replies
bandyaboottoday at 12:51 AM

I’m having a hard time imagining where this is useful. If I’m trying to assign to a property, but encounter an intermediate null value in the access chain, just skipping the assignment is almost never going to be what I want to do. I’m going to want to initialize that null value.

show 6 replies
peterashfordtoday at 1:32 AM

I'm a Java fan so I'm contractually required to dis c#, but actually I kinda like this. It reduces boilerplate. Yes, it could be abused but this is what code review is for.

show 1 reply
rkagereryesterday at 11:44 PM

More concise? Yes.

More readable? I'm less convinced on that one.

Some of those edge cases and their effects can get pretty nuanced. I fear this will get overused exactly as the article warns, and I'm going to see bloody questions marks all over codebases. I hope in time the mental overhead to interpret exactly what they're doing will become muscle memory...

show 5 replies
vjvjvjvjghvtoday at 12:52 AM

It's starting to feel like C# is going down the path of C++. Tons of features that introduce subtleties and everybody has their own set of pet features they know how to use.

But the code gets really hard to understand when you encounter code that uses a subset you aren't familiar with. I remember staring at C++ codebases for days trying to figure out what is going on there. There was nothing wrong with the code. I just wasn't too familiar with the particular features they were using.

show 2 replies
reactordevyesterday at 11:20 PM

Love to see conciseness for the sake of readability. Honestly I thought this was already a thing until I tried it a year ago…

I’m glad it’s now a thing. It’s an easy win, helps readability and helps to reduce the verbosity of some functions. Love it. Now, make the runtime faster…

estimator7292today at 5:50 AM

I don't really get this obsessive insistence of purging the language of null checks. (And "if(x is not null)" is not an improvement of any kind)

It feels like Microsoft just wants C# to be Python or whatever and the language is losing its value and identity. It's becoming bland, messy, and complicated for all the same reasons they keep increasing padding between UI elements. It's very "me too" and I'm becoming less and less interested in what I used to consider my native language.

I used to write any and all little throwaway programs and utilities in C#, but it just keeps getting more and more fussy. Like Python, or maybe java. Nowadays I reach for C++. It's more complicated, but at least it's stable and not trying to rip itself apart.

show 2 replies
coneontheflooryesterday at 10:51 PM

I’d rather be explicit. If the value is null then it should be explicitly handled.

I feel like this is another step in the race to add every conceivable feature to a language, for the sake of it.

show 3 replies
vivegitoday at 5:36 AM

This sounds like a shortcut, unless it isn't.

I have a feeling this is going to make debugging code written just a few months ago incrementally difficult. At least the explicit if statements are easier to follow the intent from months ago.

The syntax is clean though. I'll give it that.

tekdudetoday at 2:10 AM

I wonder if this supports a cleaner way to throw when the target property's parent object is null? With null-coalescing assignment, you can do the following which will throw when 'x' is null:

  string x = null;
  string y = x ?? throw new ArgumentException("x is null");
It would be interesting to try something like:

  customer?.Name = newName ?? throw new InvalidOperationException("customer is null");
But I don't know how the language would be able to determine which potential null it was throwing for: 'customer' could be null, but so could 'newName'. I guess... maybe you could do:

  (customer ?? throw new InvalidOperationException("customer is null")).Name = newName ?? throw new ArgumentException("newName is null");
But the language already supports that, and it's extremely ugly...
tialaramextoday at 12:21 AM

At least so far, my instinct is that we should turn this off/ ensure it is never turned on, as it seems likely to be a foot gun.

I couldn't imagine what a "Null-Conditional Assignment" would do, and now I see but I don't want this.

Less seriously, I think there's plenty of April Fools opportunity in this space. "Null-Conditional Function Parameters" for example. Suppose we call foo(bar?, baz?) we can now decide that because bar was null, this is actually executing foo(baz) even though that's a completely unrelated overload. Hilarity ensues!

Or what about "Null-Conditional Syntax". If I write ???? inside a namespace block, C# just assumes that when we need stuff which doesn't exist from this namespace it's probably just null anyway, don't stress. Instead of actual work I can just open up a file, paste in ???? and by the time anybody realises none of my new "classes" actually exist or work I've collected my salary anyway.

LelouBiltoday at 12:12 AM

I'm working on a Unity game and I'm so annoyed I can't use all of the new fancy c# features

aldousd666today at 1:20 AM

Like Ruby safe navigation operator `&` and kotlin, groovy and swift's `?`

sieepyesterday at 10:50 PM

Looks interesting & I'm excited to try this out myself. I like the more verbose null/error handling personally in professional code, but maybe that's because im still working in framework! I'll certainly be using these in my personal projects that'll be on .NET 10

show 1 reply
zulu-inuoeyesterday at 11:00 PM

I'm looking forward to being able to use this. It doesn't sound like much but those extra three lines and variable assignment is duplicated a ton of times across our codebase so it'll be a nice change

DeathArrowtoday at 5:20 AM

While this is nice, there are some long requested features like Discriminated Unions that got delayed a lot.

RomanPushkintoday at 3:07 AM

Nice feature! (we had in Ruby for many years)

billmcnealetoday at 12:05 AM

> if (customer?.Profile is not null) >{ > // Null-coalescing (??) > customer.Profile.Avatar = request.Avatar ?? "./default-avatar.jpg"; >}

Isn't this over engineered? Why not allow the assignment but do nothing if any of the intermediate objects is null (that's how Kotlin does it).

show 1 reply
drzaiusx11today at 12:19 AM

So like ruby's '&.' null safe chaining

show 1 reply
coolgoosetoday at 6:20 AM

Welcome to PHP land :P

dioniantoday at 3:23 AM

never using nulls is liberating. this is syntactic sugar for dealing with nulls. definitely welcome though, and definitely will be abused (which cant be done when nulls are actually banished)

wslhyesterday at 10:47 PM

Isn't this more confusing? Because it skip the code if the value is null and I don't think it is normal to follow the flow assuming nothing has happened.

show 4 replies
kazinatoryesterday at 11:40 PM

Null conditional assignment is bunk.

When you have an expression P which names a mutable place, and you execute P := X, the contract says that P now exhibits the value X, until it is assigned another value.

Conditional assignment fucks this up. When P doesn't exist, X is not stored. (Worse, it may even be that the expression X is not evaluated, depending on how deep the fuckery goes.)

Then when you access the same expression P, the conditional assignment becomes conditional access and you get back some default value like a nil.

Store X, get back nil.

That's like a hardware register, not a durable memory model.

It's okay for a config.connection?.retryPolicy to come up nil when there is no config.connection. It can be that the design makes nil a valid retry policy, representing some default. Or it could be that it is not the case, but the code which uses connection? handles the nil soon afterward.

But a store to config.connection?.retryPolicy not having an effect; that is dodgy.

What you need for config.connection? to do when the expression is being used to calculate a mutable place to assign to is to check that config.connection is null, and in that case, instantiate a representative instance of something which is then assigned to config.connnection, such that the config.connection.retryPolicy place then exists and the assignment can proceed.

This recognizable as a variation on COW (copy-on-write); having some default instance for reading, but allocating something on writing.

In a virtual memory system, freshly allocated memory can appear to contain zero bytes on access due to all of its pages being mapped to a single all-zero frame that exists in the entire system. Conceptually, the hardware could do away with even that all-zero frame and just have a page table entry which says "this is a zero-filled page", so the processor then fakes out the zero values without accessing anything. When the nonexistent page is written, then it gets the backing storage.

In order to instantiate settings.connection? we need to know what that has to be. If we have a static type system, it can come from that: the connection member is of some declared type of which a representative instance can be produced with all constructor parameters defaulted. Under a dynamic paradigm, the settings object can have a handler for this: a request to materialize a field of the object that is required for an assignment.

If you don't want a representative config.connection to be created when config.connection?.retryPolicy is assigned, preferring instead that config.connection stays null, and the assignment is sent to the bit buckets, you have incredibly bad taste and a poor understanding of software engineering and programming language design --- and the design of your program is scatter-brained accordingly.

show 2 replies
LAC-Techtoday at 1:17 AM

Is .NET entering its twilight years as a tech people build new things with?

I just can't imagine Gen Z wanting to start a project in C#.

I realise there are still .NET shops, and I still talk to people who do it daily, but ours is a field driven by fashion whether we care to admit or not - and C# just does not feel as fashionable as it once did

(I'm a former C# dev, up until 2020)

show 4 replies