TLDW logo

What's New in C# 14

By dotnet

Summary

## Key takeaways - **Null-Conditional Assignment Operator**: C# 14 enables ?., the null-conditional operator, for assignments like person?.PropertyChanged += Handler, skipping the block if null to avoid warnings without explicit checks. [01:59], [02:22] - **Lambda Params Ignore Modifiers**: Lambda expressions no longer require explicit parameter types when using out or ref modifiers, with a code fix to remove them automatically. [02:56], [03:23] - **field Keyword in Auto-Properties**: Use the new 'field' keyword in auto-property setters to access the backing field without declaring it manually, ideal for small logic like OnNameChanged. [05:06], [05:35] - **Extension Blocks for Members**: Extension blocks lift 'this' parameter and generics to the top, enabling extension properties, methods, and more in a scalable way inside static classes. [09:42], [10:52] - **Static Extension Methods**: Parameterless extension blocks add discoverable static methods like int.Range() on types, using generic math constraints for broad applicability. [17:42], [19:57] - **Extension Operators & Compound**: C# 14 introduces extension operators like times for IEnumerable<T> and user-defined compound assignment operators like *= that modify in-place and return void. [20:13], [23:18]

Topics Covered

  • Null-conditional assignments eliminate boilerplate checks
  • Field keyword unlocks auto-property backing fields
  • Extension blocks enable properties and operators
  • Static extensions boost method discoverability
  • Compound operators mutate collections in-place

Full Transcript

[music] Hey everyone. Uh, my name is Dustin

Hey everyone. Uh, my name is Dustin Campbell and I'm here to show you what's new in C 14. Um, I apologize that it's just me, but my regular co-presenter

Matt Sterson is on on vacation and we'll hopefully, you know, see him soon. Um,

but yeah, so we're talking about what's new in C1 14. This talk will be the same as it has been in the past in the sense that we're going right to demo. Okay,

it's just going to be code uh for the most part. I I don't like looking at a

most part. I I don't like looking at a lot of slides when I'm talking about language features. I want to use them. I

language features. I want to use them. I

want to see them. So, first up, we're just going to do a couple of warm-ups um of what's here. Uh there a couple of nice little features and we'll start right here. Um just just kind of some

right here. Um just just kind of some some some appetizers, some tasters. So

looking at this program, uh I've got a uh a person class. You know, it's typical, but it uh it has a warning here because, you know, nullable reference types. It's telling me that this could

types. It's telling me that this could possibly be a null reference. And uh

that's bad. Kind of annoying. So if I want to uh get rid of that though, I I've got to like add a null check, right? So if person is not null, then

right? So if person is not null, then you know, then we'll hook up the event.

And of course, you know, then it just moves the warning around, right? So,

it's this is one of these features that's like I I appreciate it, but when I've got an assignment like this where I've got code where I'm, you know, I'm dotting into something and then I'm

assigning it, we don't really we haven't really had a way to to kind of get around that easily to perform that null check. And part of that is because in C

check. And part of that is because in C 6, we chose not to make the question dot operator. that's been around since then

operator. that's been around since then uh work for assignments. We just

thought, you know, hey, it'd be a little confusing at the time, but that was a long time ago. I mean, we're we're eight releases past that. So, yeah, with C#

14, you can use question in uh assignments like this. And the reason we thought it would be consume confusing is because when you do that, that means if it's null, then this entire block won't

run. You know, nothing will happen here.

run. You know, nothing will happen here.

And so you might end up with something where there's a side effect uh that isn't observed anymore that doesn't occur. But um that is you know that's

occur. But um that is you know that's you know we just don't think that's as confusing as it used to be. Um so there you go. You can use question dot now uh

you go. You can use question dot now uh for assignments. Um we call the null

for assignments. Um we call the null conditional assignment operator. The

next feature I want to show just again it's a taster of just kind of small things that we do when you know there's lots of things to kind of go clean up and make a little easier. Um, and that's this lambda expression right here. Um,

an annoyance with lambda expressions sometimes is that, you know, I I love simple lambda parameters where I don't have to state the parameter type, but there are so many cases and they we've

reduced a lot of them, but there's so many cases where I do have to state the parameter type. And one of those is when

parameter type. And one of those is when modifiers are used. And in this case, you know, we have an out uh modifier on the success parameter. And in this release, you know what? Let's just not

in fact there's a there's a code fix to fix it for you. There's a suggestion and a fix. So often I see these you know I

a fix. So often I see these you know I usually don't see an out like this but often you'll see a ref where it's like you know there's some sort of you know refstruct that's building something up

and is being passed to a to as a in a delegate um and you use it in a lambda expression. So we're seeing that pattern

expression. So we're seeing that pattern more and more and it felt like the right time to go and just say you know let's just make this easier and clean this up.

All right next one. So this is a feature we've shown a number of times. This is

field access in auto properties. Now

we've shown a number of times because it it's been a challenge to kind of get this out there and deployed in in in kind of the right way. Um but looking at this code uh and I'll explain why that

is. Uh looking at this code, we have

is. Uh looking at this code, we have really a C# 10 property. This is the same code you would have written a long time ago, right? In C in the C# 10 days.

Um, in C# 3, we introduced auto properties and they're great because I don't have to declare a field, but there's this big cliff, right, with an auto property. Um, in an auto property,

auto property. Um, in an auto property, if I do need to do something small, then like in the setter for this in this example, we're going to call an on name

changed method. Um, if I do if I want to

changed method. Um, if I do if I want to do that now, I have to kind of fall off this cliff and write the whole property and declare a field. Um, and it's it's just kind of a it's it's always felt

kind of icky. So, in this this release, we're finally releasing the field access and autopopies feature. And here's what that looks like. So, I'd really like to

delete that field. Okay. So, I I can do that and we can make this, you know, a getter only auto property. However, what

do we do in the setter if I want to still do that? And actually the the C# the warning from the compiler is telling me, yeah, I need to use field. Okay,

there's a new keyword that gives me access to that backing field. So here I can set it and now it's the same as it was before. Essentially, you don't have

was before. Essentially, you don't have the backing field if you were assigning it elsewhere in code, but if it's just only there to support this property, then you can switch over to the field

property uh to the to the field access.

Now the reason this was a long time coming and finally released is um because it's it's a breaking change. Um

you can have a field called field um and it's not weird, right? Databases

famously have fields. Um and so this is a breaking change to have this keyword.

And so what we're recommending in this case is that you know you need to be able to disambiguate it. and you e either say um and copilot was trying to help me out there and and spoiling

spoiling the fun. I can say this dot and and disamiguate that this is a field access just like I would in any other code or because this is a keyword I can

escape it with an at symbol like I can with other other keywords. Okay. Um and

that is that is how it works. So, and of course I'm getting a warning here because now I'm talking to the field, but this is a uh uh getter only, you know, property. So, it was talking to a

know, property. So, it was talking to a different backing field. So, I need to come in here and make sure I'm doing the same thing. There we go. So, that's how

same thing. There we go. So, that's how this works. It's not too bad. Um, and I

this works. It's not too bad. Um, and I really love uh I love getting rid of this sort of thing. I think this is uh this has been a long time coming and I'm glad it's here in C# 14. Now, one thing I've been using these for, I've been

using them a lot lately. Uh I just want to want to share these work great for like lazily initialized properties, right? So if I want to say do something

right? So if I want to say do something like you know put out a we'll say like an I readon list of person um like maybe maybe there's a like a a collection of

relatives here or something and then I want to make that a getter only property. Um I can do so with the field

property. Um I can do so with the field keyword or I can say oh field keyword.

Yeah. Okay. And then if it's null and we use the null conditional assignment operator I can just assign it to an empty I read only list using a collection expression there. And now and

you can see that in the the uh the tool tip tells me that hey relatives field might be null here. And so we handle that and we initialize it with an empty empty list. So, this is kind of a cool

empty list. So, this is kind of a cool way to kind of do that sort of thing.

Um, and I like I've been using it a ton for that. So, all right. So, that's your

for that. So, all right. So, that's your warm-up stuff. Let's get to kind of some

warm-up stuff. Let's get to kind of some meat and potatoes, some some real nitty-gritty things. And let's talk

nitty-gritty things. And let's talk about extension members. Um, extension

members is something we've been talking about for a while, but it's also gone through some radically different designs. um we started with it uh you

designs. um we started with it uh you know honestly as soon as extension methods were kind of you know ready to go out before it was even released I know that the C# language design team

was already talking about well how do we do extension properties and this is now like you know many years ago this was back in C 3 that we put extension methods out there and you know extension

methods were they were this nice little feature for that was really in support of query expressions link query expressions um and they became this kind

of powerful massive popular feature on their own with in you know large swaths of ecosystem and libraries to kind of built upon uh extension methods. So

we've been looking at this for a while because the requests keep coming in how do we get extension properties? How do

we get extension this and that and extension everything? Um and so in this

extension everything? Um and so in this release we have kind of started that process and come up with a design that we think works. The reason it's taken so long is that it's been difficult to come

up with a design that scales to other kinds of members right methods are you know not hard to do in one sense because they they have all these extra features member methods get you can declare

generic type parameters on them and you can have you have parameter lists so an extension method I have a place to put that this parameter right here right so this is a an extension method it kind of

looks like link's you know first uh extension method but That means that like other members need to have these features too, right? You need to be able to have a if I want to have an extension

property, how do I declare an extension property with generic type parameters or where do I put that that this parameter because it doesn't have a parameter

list? Um, and so that's always been the

list? Um, and so that's always been the problem. And so in this release, we are

problem. And so in this release, we are introducing a feature we call extension blocks. Now extension blocks, I'm going

blocks. Now extension blocks, I'm going to I'm going to show what that looks like here. I'm going to copy this bit.

like here. I'm going to copy this bit.

This is the important bit of your extension method. Um, and I'm going to

extension method. Um, and I'm going to copy it up here and I'm going to take and say extension and then I'm going to put it in braces.

I'll remove that this keyword.

And there we go. That's an extension block. And what we're doing is we're

block. And what we're doing is we're saying we're taking the important bits that kind of make this method an extension method and moving them up to the top. Okay. Um, and so then I don't

the top. Okay. Um, and so then I don't need the this parameter anymore because it's it's declared up in the extension block. Okay. And in fact, I've got the

block. Okay. And in fact, I've got the parameter name source right there to be used down here. I don't need the generic type parameter because again it's declared in the extension block. All

right. And then the last bit and the reason there's an error on this source parameter is because it doesn't need to be static, right? And so what we've done

now is we've taken essentially this you know fancy static method which is what an extension method kind of is you know that we can call with instance syntax

and we have now changed the declarations so that the extra bits that are important uh that kind of across the members go up here and then this is the

signature that you would use in the instance syntax. Okay. So like this

instance syntax. Okay. So like this method right here, we're calling first and that's still calling this first right down here. Um, and I can I can show that in IntelliSense

just like so. So if I go here, you'll see that uh oops first shows right up and it's got kind of a little funkier parameter uh info to let you know what's going on there. Uh

to let you know that this is a little bit different. And in fact link is

bit different. And in fact link is actually still here linked to objects system.link. Uh so you know there are

system.link. Uh so you know there are actually the overloads for link there.

Um but here's ours and that's pretty cool now and we tried to make this so that it would always generate that same static method you would have written

under the hood. This is inside a static class and this is kind of a a container that will contain different extension members and under the hood. So under the hood it's but under the hood it's still

generating static methods into that static class. Right? Right? The

static class. Right? Right? The

containers really there may be some some metadata but it's not important from that language perspective of using C it's really you can think of them as all just kind of being on the on the static

classes as they were before. So that

means that when you need to disambiguate and you can see we might need to disambiguate here in in in in some cases because we have a link method and we have our my so off of I innumerable we

also have first right the link and we also have the one off my innumerable. So

if we want to disambiguate, we call into the static class just like we would with uh a regular extension method. And you

can see here here's that static method.

Okay? And it that's how you and dismbiguate. And so it's exactly like

dismbiguate. And so it's exactly like using an extension method before. We

just, you know, make you write a bunch more code instead.

I'm I'm joking. Of course, that's that's not really true. We it it starts to build up and add and really start to reduce code um that was there before.

Let's try let's I mean let's try another one. We'll see if I take select and I

one. We'll see if I take select and I move that in here as well. Well, okay.

Then I don't need again I don't need the this parameter and we can just do that.

I don't need the T source generic type parameter.

Okay. And um it doesn't need to be static. Whoops. And I delete the wrong

static. Whoops. And I delete the wrong bit of code. There we go.

And again, now we have, you can see that the extension block is doing a lot of extra work for us here. We've got the T source declared top. We got the parameter. Um, now we've split the type

parameter. Um, now we've split the type parameters though. Select gets to

parameters though. Select gets to declare T result. It has select is a method that has two type parameters, one for T source and one for T result. But

in the end, we still generate essentially the same thing that we would have generated before. And so you can see here that there's actually in parameter info there is a select method

with both generic type parameters that we kind of smooshed together. Um and so that's that's what that looks like. All

right. But the whole point of this was to be able to add extension members, right? Um that this is a scalable syntax

right? Um that this is a scalable syntax that we can add other members into. by

lifting up those generic type parameters, lifting up, you know, the the this parameter, now they can be taken advantage of by other members. So

making a property is easy. I just, you know, turn this into a property and we can put get around there and there we go. Now up top, this is still this that

go. Now up top, this is still this that just reverted to calling the link method, but we don't have to. We can go and there's our extension property.

Okay. and extension properties. Of

course, they can have setters um right here. And you can, you know, you could

here. And you can, you know, you could set that if you if you so if you so desire. Um little weird. We're getting a

desire. Um little weird. We're getting a little weird here. Uh but the thing about the setter is and this is this is kind of kind of reveals like kind of the the edges of what an extension really

is. is and what it's really is under the

is. is and what it's really is under the hood is that it's a static that is taking an instance of something and manipulating it or you know being able to call methods or doing something with

it. Um and so we don't really have a way

it. Um and so we don't really have a way in here to generate things that are like state on that instance that comes in. So

when you know the source that comes in here at the very top in the extension block where that's declared when it's when it comes into the property we don't have a way to say put some field on that

and so instance fields are not supported uh and probably will not be that also means that you know autopropies uh fieldbacked auto properties uh they won't work in extensions either. You can

you can write setters and you can store it on the side if you want. Um but

that's uh that's something that isn't isn't there today. Now um if I uh carry on with this, I want to talk about like the disambiguation. How do we

the disambiguation. How do we disambiguate a property? Right? Because

we have first but I've got a setter and a getter. And what we do for this for

a getter. And what we do for this for for disambiguation is we actually generate and expose to you um the methods that the compiler already would have generated for a property. you know,

properties are actually sugar around two a getter and a setter method, right? And

so, um, and so we expose those so that they can just be called, um, as they would be. And so, there's that. All

would be. And so, there's that. All

right.

Now, that's what we've kind of talked about um, in the past. I also want to talk about one other thing. Now, we've

looked at instance extensions, right?

Now, um, but uh, there's other stuff to look at. So, let's uh, let's let's

look at. So, let's uh, let's let's shrink this down a bit.

And on here we also have just a regular static method, a range method. And uh

this range method is is useful and it's useful in link. I mean where I can type innumerable.range that's very useful. Um

innumerable.range that's very useful. Um

but it's not very discoverable, right? I

need to know that in link there's this linked objects. There's this innumerable

linked objects. There's this innumerable class that I can dot into and there happens to be this helper method, this helper range method. Um and I can do the same thing here, right? I can call my innumerable.range because it's just a

innumerable.range because it's just a static method, right? Um, but I'd really like to be able to call that in a more discoverable way. That's what's great

discoverable way. That's what's great about extension methods, extension properties now is that I can press dot and get them on an instance and and get them in IntelliSense and I can discover

them. Well, if I want to do that with uh

them. Well, if I want to do that with uh a static method, it's it's also simple with an extension block. So suppose I want to say I want to dot into I

innumerable of int. Well, I could write an extension for innumerable of int without declaring a parameter.

Okay. And again it disambiguates because it's on my innumerable. But I can also type I innumerable event and that works

just as well now. All right, that's pretty cool. I could um let's see. Let's

pretty cool. I could um let's see. Let's

uh that also means though I wish though that like if I want to write this for other types of innumerabables now I'm kind of like I'm kind of stuck right I got to write a whole bunch of these. Um

but you know we can declare generic type parameters and uh and so I could say well I've got generic type parameter let's let's let's let it take a t and be really nice if I could return that

innumerable t and then well the starting value is going to have to be of type t whatever that is. And down here I see there's an error and that's because I can't apply plus+ to you know operator

to type T. But we you know int net 7 we add this generic math thing. So if I constrain to I number of t well now it

works and that's pretty cool. So now I innumerable of range works. Um but also uh inubable of int.range works and I could also say inubable of long will

also work. And that's now part of that

also work. And that's now part of that is because the one here actually, you know, this is treated as an int32, long as an in64 and in32 fits inside of an

in64. So this works. Okay. But if I were

in64. So this works. Okay. But if I were to change this to an unsigned int. Well,

now now it doesn't work, right? And it's

because this has that whole negative range that isn't part of of an unsigned int. So in order to address that um I

int. So in order to address that um I can come in here and we can just say like let's call it like range from one and uh we'll take off that first

parameter do a start here.

Okay. And uh we'll call it up here range from one there. And now it works fine. And now I

there. And now it works fine. And now I can use it with anything. Okay. And it

should work just fine. One last step though. This isn't as discoverable as I

though. This isn't as discoverable as I want. It's weird to type inable. Why do

want. It's weird to type inable. Why do

I have to type in numeral? That's not

where I want it. I want to be able to say like int.range.

And so we can do that. We can just get rid of this.

Do it like this. And there we go. Okay.

All right. That's cool. I dig this.

Let's go a little step further. Okay.

We've looked at extension static extension methods. Um but another thing

extension methods. Um but another thing that's been requested a lot are operators. Extension operators. Um and

operators. Extension operators. Um and

uh in this release in C# 14, we've got them. So imagine we wanted to write an

them. So imagine we wanted to write an operator like you know I wanted to write an operator for for um in fact for T here where it's an I number but I wanted to use the times operator. Say I wanted

to treat range as this as this vector like if I wanted to treat an innumerable as a vector and I I wanted to apply a scaler across it for example. So I could say let's make an ional t that is well

let's make a static operator um sorry operator I always mix up where these keywords go in operators and we'll call for times and that's going to be on an I

innumerable of t and we'll call that a vector and then I'm going to pass in my scalar that I am simply going to do a select

and then multiply my scaler across All right. So now let's let's go ahead

All right. So now let's let's go ahead and say loop through all this and write it to the console and we should see okay this is just

going to print out our 1 to 20. But now

if I come in here and I say add a scaler multiply at times 10 that calls into our generic operator right. Um, here's

another one. And this is this is also something else that's new in C# 14, which is compound assignment operators.

Um, which are intended for kind of types where it might be expensive to create a whole new type and you want to kind of modify a type in place. And so I'm going

to type this up very quickly. Um, and

we'll say another extension of T. And

we're going to operate on an array where t is an i number of t. And then finally, I'm going to declare my compound assignment operator. And these are a

assignment operator. And these are a little weird because they're not static like regular operator overloads. They're

instance members, and they return void because you're updating the object in place uh instead of, you know, producing a brand new object like you would from a normal operator overload. So, we're

going to type this in. We're going to say operator, and I'm going to do a very similar, you know, operation. We're

going to do it. We're going to scale a vector. Okay. So, let's go ahead and

vector. Okay. So, let's go ahead and loop through the array.

Oops.

Typing a for loop has never taken so long. And let's go ahead and just scale

long. And let's go ahead and just scale each element. Oops.

each element. Oops.

scale. There we go. All right. And now I can take this and we can say let's make a vector out of this. We'll call it, you know, we just call to array. Okay. And

then I can call times equals six. And

that will call our compound assignment operator down there.

Run this and we'll see that now should be scaling up by 42. So that is kind of the the the the nuts and bolts and big things in C# 14. I'm going to switch

back to slides real quick. Um there's

actually a lot that's new in C# 14. Uh

and a few things that we didn't look at partial events and constructors are you know as we continue to fill out that partial story especially for source generator scenarios. Um you know a nice

generator scenarios. Um you know a nice little feature around unbound types in uh name of but uh yeah extension members extension operators even userdefined

compound assignment operators. This is

cool stuff. C# 14 I hope um will be useful to you if you want to stay kind of apprised of things and how to keep up in C. Um there's the what's new in C

in C. Um there's the what's new in C site over at learn.microsoft. Um, this

is a great place to find out, you know, exactly what's new. And, uh, in in every .NET preview, these get kind of updated over time as we as we're developing the language. And these kind of go in in

language. And these kind of go in in levels of kind of your engagement and how much you even want to engage in this. The language feature status,

this. The language feature status, that's on Ros's GitHub page, uh, GitHub repo. That's really a, uh, a place where

repo. That's really a, uh, a place where you can see kind of like an engineering schedule of how language features are being implemented and who's working on them and what the status is. And then

finally, there's the C# language design repo. And that's a place where our

repo. And that's a place where our design happens in the open. Um, if

you're really interested in that sort of thing, you can read the notes. Um, you

can see the the disputes and the agreements and the votes and what we're doing next. Um, and uh, yeah, with that,

doing next. Um, and uh, yeah, with that, uh, thanks, uh, thanks for checking this out.

Loading...

Loading video analysis...