Full transcript of “Learn how to write better C# code in 15 minutes | DEM515”. Click a timestamp to open the video at that moment. Need another video? Use the YouTube transcript generator.
0:00 Thank you very much. Hey, everyone, welcome to the A-Team. I hear it's much better here than at Stage BI. Am Matt Torgeson. I am the lead designer of C#. And yeah, I work for Microsoft and we had a big session. If you didn't watch it, go back and watch the recording when you can of what's new in C#. Back on Monday night when everybody was hungry. This is like unofficially the what's old in C# talk. You will hear nothing new today. So if that bothers you and yeah, please create a stir, get up and and you know, trip over each other. There's a there's a few features in C# that we, we kind of feel people aren't using enough.
0:46 And I'm here to Heckle you into using them more or as to convince you of their benefit. And what something like they're all added sort of in recent years, the latter half of numbers version numbers of C# and they all kind of have a few things in common other than maybe you should use them more like 1 is that they are they are syntactic simplifications. They're a way to express your intent maybe more clearly. And if you're staying on the screen, none of them are on there yet. I'm going to add them in a bit so you can't get ahead like that. So, so definitely express your intent better, but also your code will be safer, more likely to be correct, and also the compiler can probably do a better job at
1:33 making them efficient for you than you could have done manually. OK, so that's why we think when they're appropriate for you, you should really adopt these features. I mean, you're just going to go right in and show you some, OK, The three features we're talking about today that I picked are paddle matching records and collection expressions. OK, so let's get right in. Had a matching first. So what I have here is like a traditional type hierarchy. Just imagine there's more than one derived class, right? It's type hierarchy. And it sort of traditionally does. It's like we have this little random method and does it with virtual methods so that each class can do its own thing for rendering. That's classic OO style. But often times, especially nowadays with the cloud and everything, the data and the functionality don't live together.
2:18 So what do you do if you have to write the random method from the outside? Well, so essentially what you want to do is you want to you want to you want to do this right? You want to call render from the outside on a person like what does that look like? Let's let's implement that's real quick. So we have the render method here. It takes a person P and what should it do? Well, to for starters, let's just grab the stuff that person dot render does. Let's just put that in there. And now we're coming in from the outside. Yeah. So we have to dot our way into the person. That's fine. We'll do that. And then yes, there are some. That's just one derived class for demo purposes. We only have 15 minutes and so we should take care of that when it's a student, we should do
3:04 this instead. OK, so now we'll have some if then logic. I'm just kind of doing it the old fashioned way. If the person is actually a student, the is expression has been in C# since forever. Then do that instead and or else, you know, return the default thing. So yes, we have to say P dot here and P dot here and P dot here and we're done, right? Well, not quite because of course, a person doesn't have an ID, only a student does. And we could cast and stuff here. But now this is where patterns get start getting interesting. The is expression as of a certain C version doesn't just take types, it takes patterns on the right hand side. And what are patterns?
3:49 Patterns are sort of like a a question or test of values that can either match or not. You try to match it. If it matches succeeds, then is expression will be true. But also and that you can say a type check is an example of that. But also you can extract information from it when it's true by way of new fresh variables, right? So this S will contain the person as a student already strongly typed to be a student if this test succeeds, which means we can say S dot ID here and things work out. So patterns were really cool. We added them to is expressions instead of just types. You can have all kinds of patterns. We'll talk about the other kinds of patterns. The if we imagine to have lots of derived classes or other things we want to test, though, it's probably
4:35 better to do this with a switch. So that's turn this into a switch statement. Let's switch on the person. If I could spell switch, there you go. And let's turn this into a switch expression. So where is expressions used to only have types in there? Switch expressions used to only have constants. OK, but now we can put patterns in those as well. So a case can be a pattern followed by some statements. Or again, we can do the default down here. Let's put a curly and hopefully it all indents. There you go. OK, so now we can have patterns and switch statements as well. So all of a sudden we've unified switch and is to be able to do more tests than before.
5:22 Now we also added a new flavor of switch, which is the switch expression for when you just like you want to be more stylish, I guess. And when they when the branches are results that you want to produce that you can express in one expression. So let's go and just take the just take the just take the suggestion there and turn this into a switch expression. You haven't seen them before. Same structure as a switch statement, just different syntax. So P switch there says we're going to have a switch expression. And inside the curlies, it's a little terser. We didn't say the case keyword, but we still have a pattern. We use a little fat arrow instead of instead of nested statements, but that's pretty much it.
6:08 And each of them has an expression that's a result if that pattern matched. And we'll just go in order like we do in a switch thing. OK, so far so good. I mentioned there are other kinds of patterns and I'm going to the general idea here though before we get started on those, is when you, when you use switches with patterns even more so than is with patterns, then the compiler really gets in there and tries to optimize your code for you. So it's going to do its best to evaluate things in the order that is the most, that's the most opportune or the most efficient. It's going to really go to town on your code rather than you having to manually figure out the best sequence of the ifs and nested ifs and all of that stuff.
6:54 OK, And it's also going to check more things. First of all, I want to fix a bug in my code. I'm indexing into the first name there as a, you know, string, a list of characters. But what if it, what if it's empty? So let's add another case. Actually, let's turn this into the when it's not empty case. When you're in a switch statement or expression, you can put a when clause that's simply like a boolean thing that says, oh, what to do next? So we'll do this when first name. Oh, when, Yeah, when P dot first name dot length is greater than 0. But there are also other ways we could do this when instead of just having, oh, instead of just having a a default pattern here, we could, we could use the patterns more.
7:39 But before we do that, notice that little, the little warning here. It says your switch isn't exhaustive. It says there's things you aren't dealing with, like it's exactly when the first name is an empty string, right? It doesn't deal with that and it's actually telling me. So this is one of the benefits. One of the ways you make a code safer is that you get told these kinds of things. Let's so it's telling me, OK, let's go at that actual default case that always, you know that the actual fall back. Let's just grab the last name there. Oh, that was not an_There we go and. Another thing like kind of the opposite of the exhaustiveness checking let's. Swap these two branches for a second what. You'll see if you squint is there's there's a red squiggle on this one here this. Is not the best example because they're kind of obscuring each other saying hey you, can never get here so.
8:25 You also get reachability checking in your in your switches that's. Also really good when you have complex logic so. So let's switch him back. But then let's try to examine just quickly and you won't. This is just going to fly by. But there, there are many different kinds of patterns. I'm just going to show a few. We have the one that we call a property pattern where you can actually select one of the members of the incoming value, the incoming person, and you can apply patterns recursively to it. So I could just apply the var pattern that says just take that first name and give it a name like F and then use that in my code here. And I could have a recursive property pattern getting the length out like that as well.
9:11 But we could also try other things. We could say where or you could say first name dot length and then try to do things on that. We could say first name that where first name dot length is greater than 0. And then we don't need our when clause at all because we are expressing it all in a pattern. That's the relational pattern. Or we can say actually where it's not 0, where it's taking the 0 constant pattern and saying anything but that. Or you could say actually that the first name itself is not the empty string. Or you could say it's not the empty list, or you could, which is a list pattern, an empty list pattern. Or you could say, actually it is a list, but it has at least one element that we're applying a pattern here to say has an element there and maybe
9:58 some more. And actually you could use a var pattern here to say, actually, let's call that first thing in the list, the initial that's a first character. And let's just use it over here directly, right? So you you can use pattern in many in innovative ways. I'm not saying you should do all that I'm doing right now. I'm just saying there are a lot of options getting your code safer, more expressive, more compiler checked and more efficient with patterns. So that's it for patterns. Let's talk about records briefly. We if I took this line of code and copied it and said like meds 2 here and I compare those two, say right line meds equals meds 2, then these are objects, right?
10:44 So that'll be a false like because objects compared by reference by default. But sometimes you really want, you want the object hierarchies and so on. You want to stay with classes, but you want them to represent values. You want them to present information, not like a stateful object that evolves over time. And that is just C Sharp's defaults for classes are just completely wrong for that. Like they they are so leaning into the reference based stuff. But if you want the value based interpretation, then all you have to do is to make them records. So we can say record class here and here and all of a sudden, actually let's get rid of those render methods so we don't, so we can have it all on the screen. All of a sudden we are flipping the defaults. Essentially we are implementing a bunch of value based functionality on these classes, one of which is that these two will now be equal.
11:30 It's comparing the contents rather than the references, OK. And even one of the nifty things is that even though these are strongly typed as person here, we don't know that they're students. Pilot doesn't know that they're students. The way this is implemented will actually dig all the way down and compare even the hidden ID field. OK, I'll just say it's somehow work virtual and you don't have to worry about how implementing value based equality in a hierarchy is super hard to get right. And once you start changing your code, it won't get messed up if it wasn't already. We just do it for you and we do it correctly. You never have to worry about it again. Other value based functionality is that we we actually implant a pretty nice 2 string, so it'll just list out all the stuff that's in the values inside.
12:15 But one last thing is that when you have information like this, when you want to update it, you don't mutate the information. You create another, another record, right? This, this is Mads with a with a last name, Torgerson. But if I wanted to update my first name, I could say it's Mads with and then object initialize the syntax to say last name equals Nielsen. That actually used to be my last name many years ago. And now I get a copy with that thing changed. And again, it's a full copy if I write this one out. That's actually just this one. So let's run the code. If I write that one out, then you will see that it again, it's not based on the static type that it's a person and it doesn't create a person.
13:02 It does create another student, right? It does the runtime copy of the actual type and all the hidden values as well. It's sort of virtually supported so that the new student has another last name, but it's still a student. It still has the same ID. OK, we do all that for you. Value based use use records. OK records. Also, I'm not going to go into this much. Records allow you to actually if you have a primary constructor on a record, we we will automatically implement a a a property for you as well. This does change the code up here. I do have to provide the ID as a, as a argument. Then instead of of in the object initializer, we can
13:49 just get rid of this one, this part here. So, and when they're, when they start to chain it, it, it looks like a really nifty shorthand. But when you start to chain down a deep hierarchy, you, you start to accumulate parameters because you have to take them and pass them up the chain. But sometimes that's a that's a nifty thing to do. It also gives me an opportunity to have a parameter that I can instead say, let's instead here grab a decimal array and call it grades, right? This is Matt's grades when he was in university, maybe. And then how do you provide an array? Well, I guess I'm going to do, let's give myself at least one good grade and then some other ones. Let's just do this, right? So that's how you initialize an array.
14:35 OK, But actually I wanted to be a list instead. Now I have to go and do it differently. Now I have, maybe I can use, maybe I can use, I can use. So nowadays you don't have to say the type when you still have to use a different syntax to create that. And if it's an immutable list like, who knows? Because now I can't use object or collection initialize anymore because they're immutable, you can't mutate them. So instead we have collection expressions which work for all kinds of collection types. They do the most efficient thing that the compiler can think of. They work for mutable types, they work for mutable types, They work for interfaces. We're going to pick a concrete type for you. We're not going to tell you what it is, but we promise it's good. And of course, they work for array as well. So the last example where unified clean syntax gives you the most efficient implementation, probably better than you could have
15:24 done before. Go use these features. Come ask us about these features and anything else you would like. Fred is here for my team. Meet us by the side. Thank you very much. Hope this was helpful.