-
Notifications
You must be signed in to change notification settings - Fork 85
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
add8d37
commit 28c71a1
Showing
3 changed files
with
5 additions
and
14 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
28c71a1
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
C# 8 Nullable reference types
C# 8 is around the corner and contains a feature I've been actively promoting for a few years (when Roslyn was in Codeplex!) to distinguish nullability not only in value types (
Nullable<T>
) but also in reference types (usingNullableAttribute
, flow analysis and warnings).The idea is to reduce the number of NullReferenceExceptions, most of them produced by the lack of formality in declaring whether a property, parameter, return type etc, accepts or not null values.
A few links to introduce the feature:
https://blogs.msdn.microsoft.com/dotnet/2017/11/15/nullable-reference-types-in-csharp/
https://msdn.microsoft.com/en-us/magazine/mt829270.aspx?f=255&MSPPError=-2147217396
Implementation caveats
The general idea is clear, if you want a value, whether reference or value type, to be null just put
?
in the type. This also works inside generics, likeList<string?>?
is a nullable list of nullable string.But while nullability with value types is rock solid, because there is a concept of
default(T)
value for any value type, the same is not true for reference types. For example:Even more, the full feature is opt-in for old projects and based on warnings, while is opt-out for new projects.
That means, null should not be in not nullable reference values, and the C# compiler is going to make his best efforts to avoid this, but in reality at the CLR level they do fit inside the variable, and in fact you can put a value easily if you want:
In practice this is not an issue except in two cases:
List<T>
where elements are introduced one by one.When is this comming?
You can start using it by installing Visual Studio 2019 Preview and using the NotNull branches of Framework and Extensions.
The whole thing will get merged the 2nd of April when Visual Studio 2019 is released https://devblogs.microsoft.com/visualstudio/
How it works in Signum.Entities
The way we define Entities doesn't fit nicely with the current implementation for two reasons.
We don't define a constructor. We typically create an empty entity and we let the user field the mandatory fields, one by one, in the UI. This is an implication of having the same entity for the UI and for the DB. An advantage that I don't want to lose. So we disable this particular warning (CS8618) in the entities assembly, feel free to disable it as well in other assemblies or particular regions of code if it's too annoying for DTOs or other classes were you prefer using object initialize instead of a constructor.
Entities are typically invalid when they are created, and validated the first time you try to save them. Still, I like to be able to express whether something is nullable in the DB directly by placing a
?
in the type.This means that all the guarantees that c# 8 nullable reference types bring, are just disable for new entities, in exchange, we can use the feature to know about whether things are mandatory and not null in the database, and we don't need to use
!
everywhere after we retrieve and entity or make LINQ queries.Example:
You can check this commit signumsoftware/southwind@d61f242 to see how the diff before and after C# nullable reference types in Southwind.
List of changes:
NotNullValidatorAttribute
is not necessary anymore, if the type is not nullable then there is an implicit NotNullValidatorAttribute added by the franework. If for some reason you don't want it, you can place a[NotNullValidator(Disable=true)]
attribute.StringLengthValidator
doesn't haveAllowNulls
property anymore, the implicitNotNullValidatorAttribute
handles the empty string case too.NotNullabeAttribute
andNullableAttribute
have been renamed toForceNotNullableAttribute
andForceNullableAttribute
respectively. They allow you to set nullability in the DB differently as in C#, typically not necessary.Properties that before didn't have
NotNullValidator
, or hadAllowNulls = true
now just get a?
in the type.How it works in Signum.React
I've used the oportunity to simplify the conversion between C# and TS so:
Gets translated to just:
As you see, the mapping is quite simple, if there is a
?
you get a| null
, whether is a value or a reference type. Also note,undefined
is not allowed anymore... but new entities the fields are undefined!!.This is consistent with the lack of guarantees for new entities in C#, and prevents the error of assigning
undefined
(instead of null) to reset a property and then doing nothing becauseundefined
is not serialized.How to migrate
Note: I set warnings as errors to make conformity with check mandatory.
Also you may need to replace, if C# 8 is still in beta by the time you do this.
Finally, you need to remove the CS8618 globally in your Entities assembly (and optionally in the other assemblies to)
?
to the types or, sometimes,!
to expressions when you feel smarter than the flow analysis.Conclusion
Finally the nullability story is simplified, just write
?
for nullable fields, doesn't matter if they are value types, string, entities, lites, MList or the elements in an MList.The only thing to keep in mind: all is a lie, you could have nulls inside of reference types if you want it, and it will happen for new entities both in C# and TS.
Enjoy a future free of NullReferenceExceptions 😄
28c71a1
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Awesome!