Join Mads and Dustin from the C# design team as they take you through new features in C# 13 and beyo...
Video Transcript:
[Music] I'm Matt ston I'm an architect at Microsoft and I'm also the lead designer of CP uh and I'm Dustin Campbell I'm an architect um on net tooling and also a member of the C language design team and work a lot with this guy yeah so we um uh we have doet coming out today net 9 and that also means that we have C 13 C 13 um meeting the world today and we're here to tell you a little about what's in this version of c um we uh want to start by taking you on a very brief tour of how you can stay updated on what's going on with C like everything else here it's open source yeah so I've got a couple of things up here on my laptop um that should show so so just a couple of ways that if you're new to the c universe or you want to get involved or stay engaged we have a few examples of ways to do that at kind of various levels of Interest right um this first thing this is learn. microsoft. com this is the what's new in C 13 so if you want to just find out what's uh what's in the product for C 13 and get documentation on these things and how they kind of play in there's all all of it is here and uh um this is kind of a great way to kind of kind of just get an overview of the languages that have shipped right that is true um however language features that have shipped yeah the features the um um as we go like the the C 14 cycle essentially starts now and as we go we tend to put language features out in in preview um and we actually have one that we'll be showing today that's already going to be in preview um and we we try to keep the what's new page updated even with um preview documentation of preview features as well so it is a good place to keep an eye on c as it evolves over the year um if you want to um another spot you can look at and uh and this is on the uh on GitHub in the rosin repo which is where the the C and Visual Basic compilers and IDE Stacks uh all the code lives and where they're in active development and this is the the language feature status page it's in the docs folder um where you can see the nitty-gritty on like who's working on what and uh what we're looking at for you know C 13 and then also kind of what's happening for for next and if you would if you if you're interested in seeing branches merge this is where to go right uh so it's kind of a yeah yeah the working state is really everything that we currently have engineering activity on like somebody's building um as a c language design team we work on even more things that haven't yet kind of started being implemented um and so the place to go if you really want to engage with the uh C design is that we have have separate Repro C Lang for um for the language design itself of c and this is again it's in the net uhet foundation on and on on GitHub and uh it is um this is where notes from our language design meetings are posted this is where proposals are posted there's um issues and discussions and we're actually you know this is also an active development though it's not necessarily the code development but it's where kind of the the nitty-gritty of the the language design itself happens so if you want to get involved in some way or engage with that provide feedback discuss with you know the team or you know and other you know like-minded you know folks uh then this is the place to go for that yeah and there is quite a a lot of community contribution in this repo as well to the design of C as opposed to the implementation we saw before absolutely but I guess we're here to look at code yeah let's talk about um many of the like there's a long list of of features in C 13 um and by the quotation marks I mean that some of them really feel like just bug fixes we're going to show some of the some of the more interesting ones um but as you could see on that um language status page and and the W's new um there's there's plenty yeah there is plenty um and and in a way I mean we've been kind of looking at the features that did land in C 13 and it feels very much like you know in a way um almost not necessarily small features but features that might feel smaller but they are there to just make genuinely the cons consumption of code just better easier make sure you're doing the right things you know and you know putting you on the right path giving type authors and Library authors the right tools they need to help you get on the right path as a you know for consuming apis yeah so it's it's kind of there is kind of like a theme to it even though it kind of feels a little grab baggy but I like yeah I like this idea that it's very it's very light on new syntax but there's a lot of semantics sort of behind familiar syntax if you want yeah yeah um starting with this one here we pams yes this has been uh this has been in the language since C 10 yeah pams has been around forever and it's always been about arrays right so you can have pams array and that means that that call up there on the first line um you know you you all know this just uh packages all the arguments um for that parameter into an array that gets created on the spot and passed on to uh to the method um and almost immediately after this shipped we're like why couldn't we do you know pams I inumerable and then net 2 came out c 2 with generics and they're like well why can't I do one with prams i inumerable t of te yeah um and uh that's that's been a maybe the oldest ass you know a lot way to be clear and you may want to show this like it the thing you pretty much have to do and have had to do in tar since c 2 if you want to have a general API that takes all kinds of collections but also is params is you need two overloads the general one that takes I of T and then an array one that is params because only arrays can be params you can kind of you can kind of guess where we going out there um this gives you the way to kind of like have something you can if you've got a list in hand you can pass that and it will be it it will get you know make its way into the I inumerable of T and then in the other one you can pass a list of arguments and they'll get into the prams and often you know this is kind of implemented funny you wouldn't normally do this you usually you take the prams one and you you'd use that to call the I one but it seems like every API that wants to expose prams ends up doing something like this as well yeah um and we've we've sort of had it on our list for well over a decade to do something about that but then there's like well if we do PM iable then what about other people will immediately be like okay perams other things right and so we we kind of left it alone uh but last year when we were releasing C 12 a very consequential feature came out which was uh collection Expressions my favorite feature in years right so to be able to surround this with brackets and just say this is now a collection that is not opinion exactly opinionated on the type of that collection instead it gets it decides what the collection is going to be what the type of the collection is based on its surroundings right essentially so in this case it would be we could uh uh this is a way that you could also pass it in here and and get a um you know like and it looks like very similar to passing per amps right you know but really close with just these square brackets yeah but but the big difference with collection Expressions is that you can and some of you might not have picked them up yet please do they're awesome is that you can pass collection Expressions to almost any collection type right um there are standard ways that they will work they will work like oldfashioned collection Expressions if or collection initializers rather if you don't do anything else but you can also um set things up in your API to to tell it what to do to create your collection in a smart way and and the compiler special Cas is a lot of common types and so and it's really smart it can create from the same syntactic expression it can create all kinds of different Collections and it'll do it'll do a really good job at creating um efficient implementations of interfaces and that kind of stuff now in this case overload resolution is going to make it so it calls the array still but if I delete that and I can delete that now because I've got that collection expression up there it will produce the compiler will generate a type that's very very efficient that implements ion numerable for you so you don't have to go and say I'm newing up a list or I'm neing up something else um to pass in here but instead it will new up something that Implements iable of te um so that it can be pass here successfully and efficiently and so that little extra step we took in t shap 13 is okay now we have Machinery to create all kinds of different collections from elements from a list of elements couldn't we just do that with pams as well could we just allow pams on all those collection types and have it do the same thing and it turns out we can um and so this of course is legal in C 13 we have prams for all sorts of things um and basically aot essentially the things that you can create with collection Expressions um because we know how to build that Machinery now so if I delete these brackets it works just fine and it's going to do essentially the same thing under the hood as if you passed in a collection expression beautiful you can pretty much think about it as pams is just the compiler putting those square brackets around the elements that you're listing yeah as arguments right so it's um it those two features are very closely connected absolutely now in this case because it's I inumerable we're going to be allocating something on the hood under you know under the hood to do this um but and there are cases where you don't want that right and so you might uh wonder what other things we could do with perams um and you know if I wanted to do something a little bit more efficient using some of the span based types that we have that's also allowed right so I could type you know Span in here that works or I could use its you know readon cousin um um to pass in you know arguments that are you know essentially could be from some some hpe allocated type it could be from an array or they could even just be allocated on the stack this could point a memory on the stack so that you're not making any objects to pass something in that can then be for eached over like this it's actually and this is like probably the the the second longest request we've had for for pams is for the span types um because the compiler can choose to allocate that span on the stack right and when and when it does you know no no memories wasted and it's actually very similar to if you had had all those parameters individually in your signature they would also have gone on the stack so this is this is a thing that sort of from a memory layout performance perspective mirrors uh multiple parameters the best and there's another piece of this that I think is really interesting which is that you know overload resolution would like to say that in this case you know read only SP is a better overload because it would Implement ion numer oft but of course it it doesn't it can't and so um it's a it's a it's a it's a restruck it can't do that and uh I Ru is not suitable but still we end up picking the readon span type here and this is part of another feature of C 13 that is kind of like you know you know sort of I would say kind of one of those things it's really under the hood you won't see it you know pop up but it will help you do the right thing yeah so we we we essentially embarked on a new story arc if you will in C for um for for spans that is first class spans like even though um spans have been around now for a long time um I mean a lot of people they have weird restrictions and so on because they have to live on the stack and so so a lot of people don't use them directly but we want to make it so that you can get a lot of benefit uh from them without having to understand too much of that machinery and so but but the language itself has kind of kept them at arms length for a bit like there are conversions from array to span to readon span but they're not in the language they've been used to define conversions on the span types and so on and that means spans haven't felt fully integrated in the language um but we have started down and there'll be more in C 14 making spans first class and the the the sort of down payment on that in C 13 that you see here is that we make spans better than arrays and all the interfaces implemented by arrays in overload resolution right I mean it is the more efficient way generally of doing that and so it makes sense um all right so another thing about pams about prams collections as we said you know we can do an i inumerable okay we can do read only span but we can do lots of other types and so there are also ways with collection Expressions to you know expose them to uh collection expressions in such a way that they they can the collection Expressions knows how to build them um one of the types that's been kind of uh challenging um I think I even showed an analyzer on this years ago in a what's new on c talk but the idea that like a mutable array is a is one of those types that we use a lot but we especially use a lot but it can't be used with the old style collection initializer right where I knew up and put the curries and add the elements because under the hood a collection initializer just calls ad methods but the ad method on an immutable array of doesn't mutate it it just makes yeah it just makes a new IM mutable Ray so it doesn't work um and so we made sure that when we added um you know collection Expressions that we could support types like these as well and that means they're also supported with pams um because of a special attribute you can add to make your collections work with collection expressions and also with prams yeah so yeah so you really have a lot of freedom now in how you use pams yeah um of course you want things to still um you want overload resolution to still to still work SC yeah um and this is one of these things where it's like oh gosh what's wrong uh I have no idea you know and and even you know these sorts of these sorts of overload resolution problems of like now we've got an ambiguity um of course in this case um we we have pams and three different overloads and you probably wouldn't have that we could fix it we could fix it like this right by just saying yeah we probably want the the read only span to be the Pam one CU that's the most efficient of them yeah and so that makes the most sense right and so then but then if I add this if I make it if I was passing in a collection expression which could be either an immutable array or an numerable or a read only span we're right back in the same problem yes and um and this is a situation that you sometimes find yourself in if you are if you are consuming or especially if you're writing a library you're maintaining a library and adding to it over the years and you want to add new overloads like in this case the this might have been around for a while but the param uh the readon span overload is new right you want to add that for more efficiency for your users to pick that up when they compile but now you've introduced ambiguities yeah and and and the language just didn't understand the rules of the language were not enough for that particular finetuning use case you had you were like okay I maybe I've got some API that we've got a better way to do it in in this you know whatever the organization or application and so you want it to work and it doesn't work and there's an ambiguity and now you got to do other things like at change the method names stuff that like you're trying to fit into the rules right yeah sometimes the the language can't make rules that fit everyone um and you kind of want to be able to have some agency when you evolve your API to make it so that the user of the API just gets the right stuff and doesn't get presented with that especially that we don't break their existing code maybe that didn't use to be ambiguous and now will but also like that they get the right things um even when those right things are different from what they they were before you you make the change so C 13 brings something here for that API author even if it's an internal or public API to be able to come in and say to do the fine-tuning to say the rules didn't get it right for me or didn't do what I expected um and I want to fine-tune it and so you can use this new overload resolution priority attribute and what this does is it essentially says all the overloads here are they have a priority of zero and so if I want one to be picked above that maybe if if it's a better a better choice then I'm going to give it a higher priority and that actually fixes the issue here so now it does pick the readon span which is cool it is very cool yeah and sometimes of course what you want is for a you don't want um a particular overload to kind of step in front of the others you want one to step behind the others yeah and so you can use negative numbers meaning that you have four billion of these available to you is uh as you build out your applications um but the you know using a using a negative int here and now um this actually goes below iable and mutable array and there's still no error and that's be but it's it's choosing a mutable array because now regular overload resolution is kicking in and saying at that top level and saying well okay a mutable array is a better choice than ion numerable because mutable array implements ion numerable yeah so the way and of course you um it's not just a matter of positive and negative right you can have use different numbers and they they relative priority use prime num you can do whatever I would probably use squares yeah par of two would run out quickly would just the 32 um though that's actually kind of a joke because I kind of feel like this should be used sparingly yes right this is used for that fine-tuning sort of uh situation where you just need to get something to choose get the rules to choose the right thing you just need to subvert the rules slightly um if you're if you find yourself uh actually using 32 of these um you might rethink things uh it it would still be a smell right usually you want your API to by and large work by the rules and not against the grain of the language yeah it's it's mostly but not not necessarily only but it's probably mostly situations where you are evolving an API and you want to keep existing code happy even while you are able to take Bolder steps like for better performance like move add a new overload and move the pams to it that kind of stuff yeah now this doesn't if I recall um this doesn't actually have situations where somebody could add an extension method and now subvert your type or something like that it's only the overloads within this type right yes yeah yeah exactly the priorities are only used between um members of the same type and the way you know essentially the way it works is it takes all the applicable members it it finds the highest priority among them and only the ones that have that highest priority get to compete for being chosen as yeah so if if the top one succeeds then it it wins right but if it doesn't apply then it would fall down to the next priority and let them do it out um yeah yeah which is great and so and so you can still even though you have a lower priority you can still get pick sometimes if you're the only one that fits the uh that fits the call yeah yeah absolutely okay um I think let's go and look at some other code sure that's uh that's that's three features down in C 13 yeah and we're coming over very little syntax so far very very little new syntax uh actually no new syntax yeah not not so far yeah um now here we are in this file and it is uh oh it takes a little bit to absorb uh but we've got uh uh a a a type called my file that's partial um and in here there's a partial method think back to like C 2 to remember your partial methods um and uh and down here is the implementation of that partial method now part partial methods have always been used for codenation scenarios yeah right and so they are ways that you can say here's you know a method that I want to provide an implementation to in a partial part um and they're used more now in the world of Source generators um you know we've had them for a long time but they're using Source generators as ways that you can in your type you can say oh I I here's a hook that the source jator can fill in implementation for or the vice or vice versa where you know this you know you want to provide implementation and the source Jor is going to provide a hook you know so it works very well for kind of these both these these kinds of scenarios yeah but up until now the only member kind that could be partial was methods right and we've um finally just taken the step and added partial properties as well right so I can go ahead and add a partial keyword to the implementation side and I'm going to come back here and because now it's complaining that there's no definition there's no definition so I'll go ahead and add my definition I think I need a a private set because we need the accessibility accessories to match um and why haven't we done this before it seems well it seems so straightforward the the the reason we there were there were dragons but they were more like tiny lizards but we kind of stayed away from it uh for a couple years because of that because you if you notice that get private set that looks like the body of an abstract property right but it also looks like the body of a concrete property that happens to be an auto property yes and so we kind of had to make the choice to say that when you put just you know the AIS or uh keywords and semicolons is that an auto property that's on the implementation or is that a a defition a definition we and we just said okay it has to be a definition that's the only way this this can work sensibly and that means that you can't use that same that same Auto property syntax to implement it you have to do something manually um so Auto properties aren't supported uh well um hold that thought hold that thought but not in that exact Auto property syntax that you you're used to yeah so okay this is um this is again it's it's a great feature for sourcer and any code generation scenario um and uh and now it supports properties and um yeah and honestly we are we already get requests for supporting partial other member kinds now so let's see as the scenarios kind of weren to work it might be very little work um we we'll probably keep looking at that just generalize there but for now you got methods and properties now yeah that's fantastic um and that brings us to kind of another feature that we have here we have um and here we're locking some data we're using this classic lock keyword that's been again something that's been in C since C 10 um not new syntax again um something that's been there very for a very very long time where in c 1 and net one you know we decided that uh from the design was that every object could be something that could be locked on right so any any object could could uh could be used with the monitor classes and in in uh net um and so that pattern is the one unlock but since then we have had no limits on you know putting new locks into net there are so many locks to choose from um and uh and for various uh situations in hindsight maybe it wasn't the greatest idea to enshrine the first idea we had about locking into the language itself and kind of hard quoted in because it kind of almost created an adoption blocker for the other oh yes that's a they're at a comparative disadvantage just because this very early first one is is embraced and there's nothing wrong with it per se um but um but we can do better and yeah and and I would say that now we have a new idea for a lock yes and that's what this feature is about we've got a new lock that's in inet and it's not called semaphor slim or anything like that no nothing like that it's called lock um it's uh we are in this yeah but I changed that map lock field from object to lock the new uh system threading lock and it works with the lock keyword and that's kind of the new feature here that it's that's kind of it's it's sort of um just we allow the lock statement to be used with this new lock because we feel that this is kind of very similar used in similar scenarios yeah um but it is lighter weight than the old one the old one has extra baggage with Monitor and features there that felt like well okay um something Slimmer and lighter weight to be used as kind of the Workhorse lock for these scenarios of just protecting data and to be clear this would have worked even if we had done nothing in Char because lock is an object yes so locking on it by rights should just do the same monitor thing like um but what we said here is that's that's wrong let's let's Embrace this new lock type in the language and so when the lock statement is used on an object of the lock type like the static type lock like the compiler can see this is the lock it will generate the more efficient uh code to use it actually as intended to use that lock type as intended well that means does that mean that then if I say knew up a lock and turn it into an object that that can still use monitor that will still use monitor which is like the risk of doing something like this and so what we did you can see the green squiggle there is said don't do that please yeah um if you're creating if you have a lock object don't don't upcast it and forget that it's a lock because then if you use the result of that in a in a subsequent lock statement it won't do the same kind of locking it can get very dangerous and and silly so so that we put a little guard rails on it there so that people don't mix lock accid non loocks yeah cuz things might not lock like you think they should things might have different different semantics and do things in a weird way and of course you know the C compiler has once it's become an object and it's it's passed through we have no way of knowing that it's not an object statically yeah unless unless we build a runtime check into lock and now it starts getting inefficient and so on so we didn't do that but we we put these guard rails in right and and also to be clear this is not that we've sort of generalized the lock statement to be uh pluggable in a way or to have like it's not that you can write your own types that plug in and and have their own behavior in the lock statement yeah there's no lock pattern today right um yeah so that's not this is not that um again it's it's a situation where we we just it's that's actually a bigger problem if we ever wanted to do that and so but just for this type because we feel that it is that important that we want to do something different and if that other thing becomes a scenario that people are clamoring for and then we we could we could go there we sort of broken the seal on the lock statement now but um but for now we just embra is really good type from the from the core libraries yeah that's fantastic um all right let's talk about one of our features that's maybe not quite there but it's very very ready I've been using it a lot yes um it is a feature that right now in net 9 it is only in preview and so a language version preview I don't mean net SDK preview I mean you can go into your project file you can use net9 ga uh the general release and uh you can specify Lang version preview to get access to this feature um and we going come back to why it's in preview and not part of C 13 proper absolutely but let's let's let's show that feature that feature is uh something that we've shown a number of times often with squiggles but it really is here now and it's the field keyword uh so the ability to within an auto property to access the backing field right something that you know you with a with an auto property there's always been this big Cliff you have to if you want to do various things like um you know in your Setter you just want to say trim that value that's coming into the string you know to before it gets set to the or or just lock on something before you do the mutation as in this case yeah and that results in a huge Cliff where you've now got to okay well if I want to do those things I now need to go declare a field I can't use an auto property anymore and I must specify both accessors even though I only wanted to fiddle with one of them absolutely so we're taking that Cliff away with the F keyword is something you can use to say in accessors and property accesses to say well I am an auto property I do have a generated backing field and field is how I access it yeah now this however I can still write an accessor manually if I want to if you want to this one actually does right now because it sets a field that's different from this which is now the backing field yes um and so it's so we're actually getting a warning here that we should be using field because the other accessor does so that helps me not make a mistake and make sure that I'm using this uh this the the backing field keyword as well the field keyword which is awesome and so now that map field isn't isn't needed up there you can see it's gray out up there we're not referencing anymore so so we just get a backing field of the same type as the property generated for us um now um it may be a little excessive that we're actually locking on get oh yeah so if we men the get access or as just return field yeah if we took out that lock if we wanted to take out that lock we could do so just like this because it's an auto property I used to think that auto properties had to have like a get and a set and semicolons no bodies and really what and I thought that's what made it an auto property at least when I looked at it but really what makes it an auto property is whether you created a you had to like have a back backing field or where the one was generated for you and so now Auto properties come in this like large variation of syntax that you can use to really you know kind of lots of ways that you can use to make your property experience much nicer and be more expressive um and um and just to Circle back to the uh to the preview flag to uh well we'll get there as well but just the uh the partial thing from before now we are actually writing the implementation as an auto property yeah so but but because it has it's an auto property where one of at least one of the accessors has a body yeah it it doesn't conflict with the definition definition implementation can be can we can tell those apart yes and it and because it acts as the field keyword only it is an auto property which is exciting so that is pretty cool but yeah we should talk about why why we didn't just ship it why didn't we ship it and why it's in preview right now um and the reason it's in preview is because this is a breaking change um in fact we've talked with customers uh who have said they've gone through their code bases and said I have a lot of Fe called field right maybe they're writing database schemas and they have an object hierarchy and they're talking about fields in their databases or maybe this is like it's an academic thing and it's like oh well I have a person that has a field of study or or it's a force field in physics or it's in Star Wars whatever yeah or agriculture like actually turns out that many fields have Fields yes um so um so it's a little bit of a breaking change to start saying well inside accessors it's now a keyword yeah so sometimes this now becomes a problem yeah if you had that feel and you were referencing that and so we decided we went through many alternative designs in this feature and we're like this is the best design and this is the one that we're going to be happy that we landed on like 10 years from now this is going to be right but in order to do that we have to do a little more of a breaking change to C than we usually do and so we we sat down and said okay now's the time for us to come up with a breaking change approach for C that if that we can use sparingly probably not even once every release though let's see um and so um you can see that warning you get there that's sort of like it's not we haven't really implemented that breaking change approach yet but it we're sort of on the way there yeah so so right now it says you can refer to the existing member you can use this dot field to access the field like you would normally if you had a parameter in a field that matched um or because this is a keyword the standard way in C to use a keyword as an identifier and refer to it is with an at yeah and so uh so this is kind of what we think you know this breaking change experience which would do some of this kind of work for you and fix up the code that you have so that it doesn't break with the addition of this keyword but it's probably worth mentioning if you say this.
field that work two and the reason that works is that um field is only a keyword if it sort of occurs on its right so so we made the sort of blast radius if you will of the breaking chain as small as possible it's only when you use it as a variable name Standalone like if as if it was a parameter then it's a keyword yeah amusingly I'm getting a getting that warning back again because now I'm using a field out there called field but this one uses a backing field and so now I'm getting let's say it's kind of funny um but yeah that's so this is where we want to go with uh with field and we hope that it will be yeah you know ready to go um and and and we want it to be there for C 14 ready to go and we would like you all to to play with it a bit and for us to understand like are we are we as right as we think we are about making this breaking change and uh help us get the the the the tooling experience and everything right around it so that come C 14 we can now deal with this breaking change with confidence and everyone can Embrace this new cord all right okay we have one more thing to show yeah and um and that's we finally get to why this all is a ref struct yeah I know ref structs the thing that you know you write only if you're like David Fowler or stepen to or somebody like that right and but here we have them here I I rarely write them yeah but uh but you know sometimes I do and um one of the things that's interesting about this particular ref struct is that it has a dispose method yeah but it doesn't Implement I disposable itself yeah um and the reason for that is that ref structs you know that if I want to actually want to implement eye disposable on this and pass it to something that took I disposable net would insist on that being boxed into an object because it's destruct and then you know that an object that implements I disposable on the Heap but ref structs can't be Implement can't actually live on the Heap they can only live on the stack if you want to think about ref structs like your spans and read only spans are ref structs so they those kind of limitations they have to be on a they have to be on a stack they apply to all ref structs and therefore it would seem that implementing interfaces doesn't make sense because you can't convert to them anyway yeah but it does in fact make sense and we've now enabled it yes and of course it it finds here the the dispose method needed to implement the interface um but the but How do you use yeah how would we use it and we go back here and we see okay these methods actually take I disposable but they take it in as a constraint on T yes and this is where you can now use a ref struct that implements an interface and you can could con conceivably constrain it so I can go right here and I can type my file and all is good until it's not yeah but it's a start it's a good start um so yeah gener so generic uh kind of satisfying uh generic constraints is what interfaces are good for on ref struct that's how you can use them however type parameters by default they are not expected to be ref struct you you were never able to pass ref structs as type parameters and so they make assumptions they're like if you if you have a constraint that says that you implement I disposable well then you can convert to it and so we need to make say something about the type parameter that this type parameter actually does allow ref drugs um and the Syntax for that if you want to kind of Da very uh it's very surprising cry allows ref struct so so instead of a a a traditional constraint it's more like a a non straint right it's this uh you know kind of like a we call it an anti- constraint which seems like a double negative but it's really about this te does allow ref Str it allows extra things the and so that poses some limitations on the body of that method right because right here is a conversion to I disposable but it can't be converted to I disposable I can fix that I could use T right here and because T is constrained to I disposable and allows the rest struck I can still call dispose on it but I can't actually change you know convert it to an ey disposable because of that boxing issue and you've said that you care about not doing that in this method by saying I allow R structs exactly um now the other reason this works is because in net 9 we have added that particular uh um uh I guess anti con straint non straint whatever uh to um to a bunch of the core interfaces and delegate types right so this is this is this is implemented this is added sorry on ION numerable of T iable of T now implicitly allows can see it there in the hover it allows ref stru right there if I go over to otherwise we couldn't pass T to it yeah if I go over to you know source. net I can see you know right here in the right here in the S oh that's the wrong one right here in the source I can see it's right there yeah and likewise if I go and pick up something like action of te where you've had these delegates for a long time and now they've all been anteed to allow ref struck so that if you have something that you need to pass into you know some delegate somehow you don't have to go now you know you could do it with a ref struck but you had to go to find a new delegate the parameter type of that ref struct because you couldn't pass it to as you know t as a type parameter as a type argument and now we can and so it's yeah the I mean the the op shot of the story really is ref struct couldn't participate in any kind of abstraction that you couldn't any kind of code that was abstracted over multiple different kinds um you couldn't write you can use ref structs for it and now you can they can Implement interfaces and they can be allowed into generics and we do it all over the place so they kind of been this is also kind of like a first class thing that's happening um with ref structs that we didn't have before right and so it's enabl a bunch of cool scenarios and I think there's going to be a lot more to come in future.