This is how you get rid of null reference exceptions forever

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
>> Today, on the On.NET show. We're going to be talking with Immo about nullable reference types. This is how you can get rid of all those null reference exceptions forever. [MUSIC] >> Welcome to another episode of the On.NET show. Today, we're going to be talking with Immo and we're going to be talking about nullable reference types. Okay. So you've been on the show before, but how about you introduce yourself for the audience. You're more famous than me. >> No, I'm not famous at all. At least, I hope. If I'm famous, then probably for the wrong pictures. So I'm Immo Landwerth. I'm the program manager of the.NET platform team and I specialize in anything related to API design, the base class library or BCL as we call it, and all the fundamental types, all the things that are fun. >> Yeah, awesome. So what are we talking about today? >> So today, we're talking about a feature that you probably have heard about many times but we want to go a bit more into detail on how the feature works and why the feature is relevant for library authors and what we need know about it and specifically, the feature is called nullable reference types, which is somewhat funny because people what they really want, is the fact that the thing to be non-nullable, when you want to get it nullable. >> Yes, it's confusing. Also, so we have like 10 years ago or something like that, nullable value types was introduced into the language. So up until that point, value types could not be null. >>Right. >> So it's the opposite direction. So are these like a meeting in the middle thing? >> Yeah. The interesting thing with value types is that value types and reference types have different representations in memory. So reference types could always be nullable whether you wanted that are not. For value types, usually the way you deal with that is what we call sentinel values, like you just say zero is my null value or like you have an H property and you say well, if the value is zero then it's not set. That doesn't always work when pretty much every value is a meaningful value. In this case, sentinel values are hard to do. So nullable value types allow you to basically say well, imagine you have a wrapper type that basically carries a flag that says, is the value actually present or not, and that's basically what it is. But instead, it has a different representation. Now, that the reference type is basically a feature where the underlying memory representation is exactly the same as before. It's just that the type system is more enlightened about your intent. You can say, "I don't want this property to be null," and then the compiler can help you figure out cases where you may accidentally have it set to null when you didn't want to. That's basically what the feature is in a nutshell. >> So should I think of this more as a static analysis style feature as opposed to like run-time enforcement? >> Yeah. The run times doesn't know about this thing at all. There is obviously artifacts that you can observe at run-time like customer attributes that are applied to APIs and we will probably also expose reflection API's for you to inspect programs and say, "Is this property meant to be nullable or not?" >> So we don't have that yet? >> But we don't have that yet. So the way to think about it is yeah, it's a compiler feature. The C# compiler has special knowledge baked in and looks at your code and says, "Oh, you did an if check to say if X is not null," and then under the if we assume that X is not null because you just tested for null." Basically that's it here. >> So I sometimes hear this term used, so as you know I sit with compiler team and I hear all their conversations. So I sometimes hear this term used called flow analysis. >> Yes. >> So I think that relates to this feature. >> Yeah. That's where we look at some sample codes to make it a bit more easy to understand. So basically, what we have here is a typical program that does what you think it does. It traces null reference exception. We've all seen that many times. So what's happening here is you have a customer that has a first name, middle name, and last name and all we do here we just sum them up. Of course, if middle is not set, then we get a null reference here. If I run this in the debugger. >> Yeah. A lot of people can probably figure out what is going on. >> I'm still surprised many people don't know this. If you look at the exception message closely, we actually now tell you where the null reference exception came from, because clearly it's on this line. You know that by which of the three things is the one that was causing the null reference, and it's the middle name, getter that returned null. So you don't have to guess anymore, you can hover over all of these things where you can actually see that this is the one that's the offending thing. So the feature that nullable reference types allows you to do is to say, "Well, I expect this to be nullable and I don't expect these to be nullable." So now you can see this is critter here and this critter tells you that annotations are not available because you haven't enabled them effectively. >> Okay. >> So this is the feature you have to opt in. Even if you use the latest version of C#, existing code is the the thing as it turns out. None of the existing code is a way of nullable, so that means you generally can't say that every single time I set string, I meant non-nullable string. That's probably true like what, 85 percent of the time maybe? But not all the time. That's why the feature is something you have to opt in. So the way we can do this either by opting in the entire project or you can actually do it file by file. But for new code it pulling makes sense to do the entire project. >> This was one of those like choose wisely features. >> Yes. If you've turned on existing code basis, depending on how your code is, it might be harder or easier. >> Okay. So now what's happening? >> So basically, now what we now have here is there is a few things happening right now. So first of all, if we just zoom in here, if we just look at the definition of this type here, this type is basically saying this, "Two properties, first and last, I said they're non-nullable but they never initialized by the constructor." So well, that means if somebody just used it up, clearly, then no. >> Yes. So some few in the constructor set them both to string.empty then you would be set? >> Yeah, or the other thing you can do is you can just make them private setters, for example, and then just have a constructor that takes them as arguments. Then the only way you can get it customized if you set first and last. That would be the way to do it. But then more importantly, we also get this guy here where we say, "Well, here you're doing this thing and here's exactly that the time where, and just the reference in the middle." We said explicitly, "Well, we expect middle can be null". Now, the component tells you that, "Well, you didn't check for that." So to your earlier question of I can just do this. I could just say, "If customer middle is null else, I do this." So we would say in length. Actually, let's do this. Then we just do the simple version of it. Say, if middle is null, [inaudible] some of the first two. If middle is not null, then we use it. What you can see here is that the compiler doesn't give you a squiggle anymore because the compiler knows that you tested this for now, and that's the flow analysis part where I didn't have to introduce a local variable that is marked non-nullable. The compiler knows that in this block here, you only execute this code if middle is not null. >> So how smart is the flow analysis? So in that case, it's pretty straightforward because you have an if statement, and then you just go into each of the branches and you can know whether middle is null in each of the branches. That seems like a fairly straightforward test? >> Yeah. I'm not a compiler guy, so I don't know what the actual technology is or the actual scientific approach, whatever you want to call this, is being referred to. But basically, the compiler only looks at individual variables and whatever checks you have in place. So for example, I can have relatively complicated expressions here where I have multiple variables and certain things are mutually exclusive and whatever. The compiler doesn't track them because it's not a full theory improver where we satisfy each of the variables and conditions and figure this out. It's simple in the sense that there are things like this we detect, other things that we also detect. If this is null, you can say Environment.FailFast for example. You can say whatever. I think that might be because I probably have an older build where the APIs here are not annotated yet, but basically a fail fast is annotated, that we know that this call will never return. So basically, the program will terminate when you call this thing, which now means that if middle is null, then this code here is not reachable. So the compiler in the final version [inaudible] will not get this squiggle here. >> Actually, on that note, could you F12 that? Or maybe we're not going to see them because you're using the wrong build because- >> I'll go into the implementation. You can see this. >> There's something there. >> I probably have an older version of the compiler that doesn't know that attribute then. >> What that means. >> Yeah. So it basically annotates this and says this method never returns. >> So you explained the one which does not return. Can you explain what the other one means? Because it's not- >> This one here? >> -it's not super intuitive on the face of it. >> Yeah. This one is not what you are supposed to see. That's basically one of those things where our type system doesn't allow you to actually put question marks and exclamation marks on the actual API. So you can't say string exclamation mark or string question mark here because the CLI has no representation for this. So the way we do it is we just have a custom attribute that we apply to that and then that basically means that basically this thing is nullable. >> Okay. That's what the two means? >> Yes. >> Is there a reason that an E-num wasn't used there? >> Because if you look at nullable context, it has two different arguments. One is the byte, one is the byte array. So if you have like an IEnumerable of string, one IEnumerable of the key value pair of a string and a string, the first string might be nullable, the second one might not be. So we have [inaudible] have multiple numbers there, and it's just the most compact representation that we could have. Even if you use an ENUM here, especially when you have an array with four elements, you have to reflect the type in your head to actually figure this out. So the final version of the product will not show you this. Instead, it will just put a question mark here. That's the end, that's the intent. >> Okay. We're just not there yet. >> But that's basically how it's being encoded, if you will, in metadata. So that's one example. Another one that you can do that is very similar is you can just say, I assert that customer middle is not null. >> Or you could write is object. >> Yeah, or that; would do the same thing. As you can see, the squiggle disappears here because the compiler knows that this code is unreachable if this is null. >> Although, don't asserts only run in debug builds? >> That's correct, but if you're thinking about the feature in general, it's not meant to be a bullet-proof system. It's really meant to capture the intended [inaudible] the APIs and help you write correct code. >> Yeah. I got you. It's totally in the spirit of the rest of it. >> But if you look at the assert one here, it's similar here. We have a custom attribute applied to the parameter that says, ''This method will not return if this condition is false.'' >> Oh, I see. >> So that's how the compiler knows that while the condition is, this value is not null. So if the value is null, this call would have never ever returned. >> Yeah. So this is starting to show the more conditional nature of the feature? >> Yes. >> Do you have any other favorites that are totally wacky? >> Yeah. So the one thing that is more on the complicated side is, if you look at stuff like this where you have actually concrete types, it's all very easy. It gets a lot more interesting when you consider generic stuff because, well, generics are hard. So for example- >> Yeah, at the best of times. >> So we have this method here in the BCL. Actually, let me maybe change the color theme to something that is a bit more on the lighter side. So basically, this method here is searching the contents of the array, and then the last, it doesn't really matter if you look at this one. The first- >> [inaudible] just a tiny bit. >> Yeah. Like this. So basically, the first time you find an element where this predicate here returns true, what we do is we return you that value for this thing. But this is a generic API on an unconstrained T. So any T will work. Well, that means if the array is of nullable customers, then it can give you back a null value just fine because the array itself may contain null values. The problem now is, what happens if you don't find a match at all? [inaudible] return the value, and so we return default of T. So if now you mentioned the array you pass them as a customer array, meaning you'd say there cannot be any null values in the array. Well, it would return you a null value, but the T is customer so that technically the [inaudible] itself by saying, well, here's your null customer, but in fact I give you null customers back. So you can't express this in generics because as soon as I put here a question mark, that's not correct anymore because if T is a value type then it's a completely different representation from if it's a non-value type. So in generics, you can't just put a question mark. It's something you can't do. >> I'm sure you lost half the audience there, because I didn't follow that in completeness. >> This is just one of those things where you as a customer hopefully never, unless you write generic code yourself, you probably never have to deal with that because when you instantiate this method here, meaning you run it over your concrete datatype, the compiler on the ID will do the heavy lifting to say, ''Yeah, this thing can return a null value.'' So it will show you in [inaudible] customer question mark when you invoke it over a customer array. So what we have here is basically a custom attribute that says maybe null, meaning it could be null, it could be not null. It depends on what the T is and whether the function succeeds or not. >> So what's the value of the maybe attribute as opposed to, say, nothing? >> So if we don't say anything here, so if you do it like this, that means the signature will show you it gives you back a customer, and then subsequent code when you just say, print customer dot first name, you don't get a squiggle in the ID that tells you you may not reference because you couldn't find the customer. >> Oh, I see. >> Now, with this attribute, we say, "Hold on. We know that this method may return a null value here." So you actually get a squiggle in the ID if you just call find and just start dotting into that result though not checking it for now. >> I see. So in this way, we enable this method to participate in the model. >> Yes. >> In a quasi useful way as opposed to not participating at all. >> Yes, exactly. That's the part that is also not necessarily over super intuitive. For example, another very common example we have is dictionary. >> People use those constantly. >> So TryGetValue. So same thing. You have a dictionary, you have string and customer, and we say, give me the customer lander, and then we didn't find the customer. Well, clearly, even though if your dictionary does not allow to contain null customers, this method has to give you back a null customer. Now, that you only find because- >> In the case that that customer is missing from the dictionary. >> Yes. If we can't find by the key. Generally speaking, you're not very likely to extend [inaudible] in this case because the whole signature here is, you're saying, if TryGetValue, and then you only use the old variable if the method was true. >> Yeah. >> So we actually encoded this as well as a convention where we say, well, we have this MaybeNullWhen custom attribute that says, ''Well, this value here may be null if the return value of this method is false.'' So same thing here. Now we can actually, if we look at the- I hope I have a build of the compiler that actually holds, but let's say I have an actual dictionary here. New Dictionary String, Customer let's say, and let's say just "Dict TryGetValue." Our var c and then let's say, "C.First." >> Yeah, doesn't matter. >> Now here same thing, I get [inaudible] the events of postal elephants because we know that this thing might be not. >> Right. >> If I actually check the return value. >> Yeah. >> Now, this figure disappears. >> Yeah. >> That magic is enabled by this customer attribute because we said, "Well, the return image of the function is false, then this value may be null." If you think of just question marks, it's just more expressive enough to express these conventions. So we've worked really hard and we annotated the base stocks library to let the compiler have the smarts to do that and we really tried hard to avoid cases where the compiler has to literally special case the method. There are very few cases where we have, common example would be, Interlocked Compare Exchange with a contract is so convoluted that it will be super hard to express that. But for the most part, even environment fail fast, or debug a certain amount of special case they just have very simple custom attributes. If you own assertion library, you can totally apply the same customer to use and get the same behavior. >> So what was the process that your team used, which you've decided as a team, "We're going to go annotate all the stuff." Presumably, for the first while at least once a week, someone would come up with, "Oh, I don't have the tools to properly annotate this. We need a new descriptor of sorts. So how was that like?". >> Well, before we start there, let me just serve your wholly generally thought about annotating and stuff, because there's basically two approaches. The approach that pretty much everybody normally would take us to annotate the implementation and then by annotating implementation, you also annotate your public APIs. People ship a single DLL usually where the public API and the implementation of the same DLL that's not wholly ship. We have these what we called Reference Assemblies which are basically header files, and then we have implementation on the other hand. So one thing we thought was, "Well, maybe we can just look at the APIs and convince ourselves by question marks should be, and then just change the Reference Assemblies, because there will be much easier for us to do it to make implementation. >> Totally makes sense, do it. >> Unfortunately now, turns out we have never had a type system feature for nullable. So we honestly don't know what things can be null and what not. So we decided early on, like that will not work. Let's actually do the pop-up. Let's actually go implementation first. So we took the lowest library that we have, which is System Private Caller, which is basically the risk string leaves and end leaves. >> Yes, the core library. >> It's the core library. We basically started to annotate that entirely and we basically just literally took what's 1,200 files and said, you have five Devs. You get these files, you get these files, you get files, and then for every single file they just said "Pantanal Enable", and then they'll just fix the file out and then submitted PRs for all of those things. So as we were doing that, our goal was we will not change the code and will not add any If statements. We will basically just express what the code already does. We will not change the semantics of file. By doing that, we found what you just said. We found all these holes they try get value of where we really can't express with just question marks what the API does. So now, that means every time not the call site uses dictionaries, you would have to suppress things. That's the one thing we haven't talked about yet is that, I can also suppress warnings. So much all I have a question mark. So if I say this, and I I do this, I can now get a warning here that says, "Well, you didn't check." I can just bang this and say, "No, I know this is not now." You may think this is now, but I know that in my dictionary that always have a lender floating around. So I know that's safe to do. >> Right. This is the one that I sometimes called the Dammit Operator. >> Yes. >> Damn it, I'm right. >> So we kept track of all the cases where we used the Banging Operator, because that's an indicator that either the type system is not smart enough, or we have not enough flexibility in the type system to express ourselves. Or we just found bugs in the compiler as well. So then we started following bugs against the compiler team and said, "Well, how do we express that?" Then the Compiler team, the Language Designers, they went over there and the end result is, I can roughly show you where we are with that. There's this namespace SystemDiagnostics.CodeAnalysis. There's a few things already in there like "Suppressing Message Attribute" and "Exclude from code coverage", but all the other ones are just custom attributes that we came up with, that allows us to express the liability contract like the Tri-Gate value or the Decimal Return thing for example. These are the only things we ended up having to apply. I think if you go through core loop, I want to say it on the public API side, maybe 50 applications of those attributes. >> Okay that's actually relatively low. >>Maybe 100, something in that order. On the implementation side, we have a lot of those applications. So one of the things we do a lot in Colib, is we don't just throw an exception, because the throw statement in ILS, pretty like a lot of bytes. So we have these helper functions that basically create and throw the exception. So the compiler of course no longer knows that this method will never return. So one of the things that we apply a lot, is that it does not return attribute on all the things that construct exceptions and fold them, for example. >> That makes a lot of sense. >> The compiler connection understands that this will never return. Or the other one that we use quite a bit in implementation is everything that is generic, where we have to use the maybe null attributes for example. >> So back to my flow analysis question. >> Yeah. >> If the flow analysis was more sophisticated, then would you do not need the-- does not return attribute as much, or that's not the right way? >> Yes and no. So you could say if the compiler could do Global Program Analysis when looking at every single method, then maybe. But the problem is you have interdependencies now, right? Because methods call other methods. So it's very hard for the compiler to have a complete picture. In a sense, the whole idea of a Type System is that you don't have to do that. The whole idea of a Type System is you declare your inputs and outputs semantics, and then the compiler only has to look at each individual edge if you will in your graph of functions. You don't have to look through the whole graph. So we still can while compilation being first. We don't want to spend like an hour- >> I see. >> - for some tool to run. >> Like I mean, we used to ship this thing called Cloud Contract, whether it was a verifier that was a full theory improver that was more powerful. You could express things like: we'll have preconditions and post conditions. You can express for example that certain indices have a certain relationship and binary structure, for example. The compiler could prove or the tool could prove that your code is satisfying that or not. But we don't do that. We tried to keep it relatively simple, because it also means that if you look at APIs, it's also relatively easy for you to figure out what's correct. >> So this feature wasn't entirely just about like perfect correctness and how much of that we could afford on the schedule. There actually are some inherent trade offs. >> I think the number one trade off like I think Matt said it really well phrased, this thing is not designed to do enforcement. Well, the first time you use an API, you don't know what's null and what's not null. A good example is hash table. Hash table has this behavior where when you index into it and the key doesn't exist, it gives you a null value versus dictionary doesn't do that. It just told an exception and says the key doesn't exist. So you have to read the documentation today to know that this is the behavior. The idea here is that, because we promote these things in the type system, you get the whole experience in IntelliSense and documentation and everything can tell you what about other expectations. So there's a huge productivity gain in you knowing up front what the expectations are. That's particularly important for things where via for example interfaces and virtual methods where, well, there's a contract, but it's only written in texts which nobody ever reads. So it'll be just whatever they want to do. So the idea here is that, we tried to express the intent and we tried to keep honest people honest. But if you want to cheat and if you want to bypass things, you can figure out a way to bypass the Type Checker. But the idea is that, we don't do that. Our goal is to do best effort analysis and that means minimizing false positives also minimizing false negatives. So we think we have reached a trade-off that that is pretty good, but the proof is in the pudding. So we've done a Core 3.0. The idea is that people should opt into the feature and see whether it works for them. >> So what's our philosophy on? You mentioned interfaces and base classes, because by definition, particularly with interfaces, there's no implementation to reason about. >> Right. >> Are there any cases where you want to raise in that category? >> I compare is a good example. You basically get to values, and you're supposed to compare them. >> I see. >> So there's an invader that is zero when they're the same, negative and less when the left is smaller, and positive if the right is smaller. So the idea is that you're supposed to handle the case, where either argument is enough. >> Yeah, I know we were headed that direction. >> So the interface basically now says, "Yeah, the X and the Y that imposter you have to assume they are now." >> I see. >> So they're best both marked with maybe not. >> That makes sense because it's inherent to satisfying that interface. >> Yes. So there's something, I quality compare it as a same thing. If you call equals, one side might be not. There are also virtuals on base class that have a similar problem. So the most common example that we argued on the team quite a bit, was object.toString. So object.toString clearly I would argue the intent has always been that you don't returning null value. However, we found internally plenty of cases that we return null. Of course, nobody writes return null, but usually if you return something else. Like, you have a name property or you have a field that is destruct or something. >> I get it. >> While those might be uninitialized, so you may actually get cases where toString does return null. Now the question is, what should we do? So we have two options. You can either say all of the toString is marked as not null. That means not everybody who overrides it will now get a warning if they don't adhere to the contract. But it also now means everybody who uses toString no longer and gets a warning that the result might be null. >> Yeah. That's the problem case. >> Right. The alternative would be we mark it nullable. Which now means everybody who uses it gets a warning and the reference that radio which might be annoying. But if you look at common cases, we convince ourselves that that's the better angle because people that co-opt to a toString usually directly forward the value to other APIs that generally accept nullable radios like Console.WriteLine, Trace.Write or something like that. So you will not get spurious null warnings and then the other thing is when you're on a derive type like StringBuilder.toString, every time you have a device type, the device type can decide to mark thing as nullable but even at the virtual basis is nullable, the device type can say, "No, I'm and not nullable." That's the same thing with any other virtual method that an overwriter can always be more strict if you will, at least for the outputs you return. For the arguments you take, you can be more forgiving but not less forgiving. But that's the general idea where we looked at things and said, "Okay, what can we do to make it so that people don't miss out on warnings, but we also don't flood their arrow list with warnings that are an actionable or that we know are very often wrong." So another common example that we have is every single time you have a struct and the struct has properties that returns a reference type. Strictly speaking, there can be null because you can always have a default value of that structure where the field is uninitialized. But we said well that now means if every property and every structure is a reference type is marked as nullable, this no longer useful either because that's probably also are not true. So we said if realistically speaking most of the time you get the struct from a BCL API where the thing is initialized, we mark it not nullable. If there are cases where you constructed yourself very frequently then we mark it nullable because we'll God knows what you're doing in you code. So that's the general philosophy of guiding you towards finding null reference exceptions earlier rather than later. Yeah. >> So how about we transition to recapping how you turn this on and if there were that people should look at to get a non-video format of this. >> So there's a blog post coming that Phillip Carter is writing that has a very in-depth walk-through for every single customer attribute that we have. Why they're there and how they work. >> I think you know the URL already. >> I do know the URL, it's aka.ms/nullable-blog. >> Yeah. >> If I remember correctly. The feature is available with.NET Core 3.0 and.NET Standard 2.1 and by extension, all types implementer inside of 2.1. But you have to still opt into that. You still have to say either this, sorry, null will enable or the other option that you have is you can do it by file. So the other way we can do is we can say anywhere in the file. >> It doesn't have to be in the first line. >> It doesn't have to be the first line. In fact, I can move this all the way to here. >> Okay, for kicks. But then it's still a whole file scope. >> No. >> Oh, no it's everything below. >> Yes and you can also turn off again. You can also say "Disable". >> But you don't have to write that if you wanted to go to the bottom. >> Yeah. It's just to the bottom. It doesn't carry forward to the next file. That would be really good. >> So but in this case where you've enabled it just for the customer class, your users of that are not using the feature. Is that right? >> Correct. It controls both things. So there's also other directives you can use that control analysis versus how the type should be interpreted. But the way I've written it right now it means this is considered not null. This is considered undefined, this could be null or not null. It's basically what string needs today. >> Unenlightened? No. >> I forgot. That was a funny word for that. I wanted to say ambivalent but don't think that was. Oblivious, that's what it is. >> Okay, oblivious. >> Because it could be null or not not. So you can think of it, this is string bang; the non-null string and this is the nullable string which is the three kind of types. So you could do that too but I would rather recommend don't do that. Make a decision file by file. So what I have done usually is when I opt in existing code, I don't turn it off for the whole project. I turn it on file by file, fix all the warnings, remove all the panel enabled and enable it for the project. >> Yeah. Once you've gone through all the files. >> If you have a large existing co-opt base where it will take you basically multiple days, weeks, in our case it was weeks, to actually enable that, another way you can do it is you can turn on nullable on the project wide, immediately commit this change, nullable disable. >> Okay, go the other direction. >> Because now that means every single time somebody adds a new file, there by default in the nullable land. So you don't accumulate more debt as you're going forward. Then you just remove those guys and fix the fall out. But that's the other way to make that work. >> Okay. So this is only in Visual Studio 2019? >> Correct. >> Basically, you need to follow the.NET Core preview. So right now basically by the time 163 when it ships, that'll be the good one. >> Yeah, I forgot what the version of Visual Studio is but the other sounds right. >> Yeah. I think that's the right one. >> Of course, it also work in VS code, latest version of [inaudible]. It will also work. Right now you would expect that we have here a quick fix or something. So there's still stuff that you need to work on to provide you with a bit more of experience but the compiler feature is done as in, we believe the flow analysis the compilers does is correct, the way the attributes are emitted is correct. The.NET platform itself is not fully annotated. For.NET Core 3.0 what we have done is basically we've enabled the core library which is about I want to say 30 percent of what you get referenced by default in the console app. >> I see. So the rest will be done in.NET 5, is that the plan? >> Yes, and the other thing is we consider all annotations in preview. Because as I said, there are some elements where options are toString where there really isn't a correct answer. Their opinions on what is more helpful for the customer but strictly speaking, either one would be correct. So we made some decisions, we believe they're correct or more usable or more useful I guess but we want customer feedback on those. >> Where should they give that? >> Sorry, what? >> Where should they give that? >> So there's a blog post coming where Philip talks about this. It could be comments on the post, fall backs on GitHub. >> .NET/coreeffects. >> Yeah. Core effects or it's the feedback tool here as well would also work. So the idea is consider those previews. However, if you want library authors, like if you have a NuGet package, we want you to opt into that and start annotating your own library. Because the whole video of this feature is only as big as much of the API as the customers user annotated. That includes the framework of course but also you can solve JSON, everything else that people use. So unfortunately, this whole ecosystem will take a while to get this. Which is why they want library authors to start basically with.NET Core 3.0 and.NET Standard 2.1. Don't wait for 5.0 where everything is annotated. Get ahead of the curve from and try to get it done. >> Makes sense. Okay. I think that's pretty solid for one show. Well, thanks as always to be on the show. Yeah, this was a summary of nullable reference types with Emo who's one of the designers of this general area. Thanks again. >> Thank you. [MUSIC]
Info
Channel: dotNET
Views: 5,883
Rating: 4.8723402 out of 5
Keywords: dotnet, nullable reference types, nullability, nullable, null reference exception, .NET, reference types, runtime, .net core, flow analysis, C#, csharp, null
Id: v0aB9YCs1oc
Channel Id: undefined
Length: 38min 17sec (2297 seconds)
Published: Thu Sep 19 2019
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.