Reputation: 4405
I have always developed using ASP.NET Framework, where I used a string property this way: public string FirstName { get; set; }
.
Now I started a new .NET Core 6 project and I declared the same property in a custom IdentityUser
class.
In this case, Visual Studio told me that it is better to use nullable string
. Why does it suggest that since a string
type can be already null?
Suggestion message appears in Spanish but it is basically what I have described.
That suggestion is gone when I use string?
. Note that if I use Nullable<string>
shows a compiler error since string
is a reference type.
Just wondering.
Thanks Jaime
Upvotes: 1
Views: 1805
Reputation: 6816
No need to use Nullable<>
type. That's for value types. Just decide if you want to use the Nullable Context. You will find this setting under
Project Properties >> Build >> General >> Nullable.
You have two basic options here.
When this Nullable Context enabled, you are telling the compiler the following:
?
symbol may never be null. It must always refer to a valid object.?
symbol right after the type means that it may be null, just like old-school C# code.That goes for strings, or any other reference type. The code-analyzer to checks for this and complains. So when you declare your property like this...
public string FirstName { get; set; }
Then you need to ensure in your constructor that this property is initialized to some valid string object or the code-analyzer will complain. Or you could declare it with a default value like this:
public string FirstName { get; set; } = string.Empty
If you want to be able to set the FirstName to null -- if that actually makes sense for your project -- then declare it with the question mark
public string? FirstName { get; set; }
Now it acts like an old style reference type, before C# 8.0.
Once you turn this setting on you'll find yourself dealing with this a lot. If you have warnings set to the highest level you'll be forced to chase down all these things in your code and address them. This is a good thing. Don't avoid it. Yes it is a pain to address up front but it saves you countless headaches down the road.
I once did it for an established application and spent two full days fixing all the warnings it generated. But in the year or two since then, I have lost count of the number of times this feature made the compiler/code-analyzer catch me failing to initialize a non-nullable reference type and saved me from a potential NullReferenceException
down the line. I think this feature is priceless.
Nullable Context forces you to think about every reference type you have when you write the API. Can that reference be null or not? It makes you set the rule and honor it and is a lifesaver in a large project with multiple developers
Note: If you do use Nullable Context, you will want to be familiar with a very useful code attribute to use on some out
values for functions: The [MayBeNullWhen]
attribute. It comes in handy when you write a Try-type function to retrieve a reference type.
For example, suppose you wrote a function like this. This would work fine before but generates errors with Nullable Context enabled
public bool TryGetWidget(out Widget value)
{
value = null; // *** ERROR: Not valid if value is not nullable
if (/* some code that tries to retrieve the widget */)
value = retrievedWidget;
return value != null
}
The implication here is that the function might not succeed and return false. But if Widget
is a reference type, then it will have to set the out value
to be null
in this case. Nullable Context will not allow that. You declared the out value as out Widget
not out Widget?
So you cannot set it to null
. So what do you do?
You could change the argument to out Widget?
public bool TryGetWidget(out Widget? value)
That would make the function build. But then the Nullable context would complain if you tried to use the return value after the function returned true
.
if (TryGetWidget(out var widget))
widget.SomeFunction(); // ERROR: `widget` might be null
Seems you can't win either way, doesn't it? Unless you use [MayBeNullWhen(false)]
on the function declaration
public bool TryGetWidget([MaybeNullWhen(false)] out Widget value)
Now, your function will compile and your code that calls it will compile. The compiler/code-analyzer is smart enough to realize that a true
return means you can use the out
reference and a false
returns means you cannot.
In addition to the ?
operator there is also the !
operator. This one tells the compiler, "Ignore nullability. Assume this reference is valid" So if you had a function like this
Widget? GetWidget();
The fillowing code would not compile
GetWidget().SomeFunction(); // ERROR: Return value of GetWidget() Might be null
but this would compile by forcing the compiler to pretend the return value is valid.
GetWidget()!.SomeFunction(); // Better hope that returned reference is valid.
Upvotes: 4