>> 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]