I've been playing with the new Nullable Reference Types
(NRT) feature for a while and I must admit the biggest complain I've is that the compiler is giving you those warnings in the classes' declarations.
At my job I built a micro-service trying to solve all those warnings leading to quite a complicated code especially when dealing with EF Core, AutoMapper and DTOs that are shared as a Nuget package for .NET Core consumers.
This very simple micro-service quickly became a real mess just because of that NRT feature leading me to crazy non-popular coding styles.
Then I discovered the awesome SmartAnalyzers.CSharpExtensions.Annotations Nuget package after reading Cezary Piątek's article Improving non-nullable reference types handling.
This Nuget package is shifting the non-nullbable responsibility to the caller code where your objects are instantiated rather than the class declaration one.
In his article he is saying we can activate this feature in the whole assembly by writing the following line in one of your .cs
files
[assembly: InitRequiredForNotNull]
You could put it in your Program.cs
file for example but I personally prefer to activate this in my .csproj
directly
<ItemGroup>
<AssemblyAttribute Include="SmartAnalyzers.CSharpExtensions.Annotations.InitRequiredForNotNullAttribute" />
</ItemGroup>
Also I changed the default CSE001 Missing initialization for properties
errors to warnings by setting this in my .editorconfig
file
[*.cs]
dotnet_diagnostic.CSE001.severity = warning
You can now use your Connection
class as you'd normally do without having any error
var connection = new Connection()
{
ServiceUrl = "ServiceUrl"
};
Just one thing to be aware of. Let's consider your class like this
public class Connection
{
public string ServiceUrl { get; }
public string? UserName { get; }
public string? Password { get; }
public Connection(string serviceUrl, string? userName = null, string? password = null)
{
if (string.IsNullOrEmpty(serviceUrl))
throw new ArgumentNullException(nameof(serviceUrl));
ServiceUrl = serviceUrl;
UserName = userName;
Password = password;
}
}
In that case when you instantiate your object like
var connection = new Connection("serviceUrl");
The SmartAnalyzers.CSharpExtensions.Annotations
Nuget package isn't analyzing your constructor to check if you're really initializing all non-nullable reference types. It's simply trusting it and trusting that you did things correctly in the constructor. Therefore it's not raising any error even if you forgot a non-nullable member like this
public Connection(string serviceUrl, string? userName = null, string? password = null)
{
if (string.IsNullOrEmpty(serviceUrl))
throw new ArgumentNullException(serviceUrl);
// ServiceUrl initialization missing
UserName = userName;
Password = password;
}
I hope you will like the idea behind this Nuget package, it had become the default package I install in all my new .NET Core projects.
Plain Old C# Object
. Just an object like any other, without inheriting from any special class. I suspect you have a different question. How to create DTOs -Data Transfer Objects
– MoueThis should be the responsibility of the one initializing the class to ensure that the properties are non-null
the class should be in a valid state always. Ifnull
isn't allowed, the property should never be null. Perhaps, instead of a basic string you should use a specialized class for the URL that can have a value ofNone
or` Missing`, like the Option class in F#. C# 8 allows you to write such classes and check them with pattern matching – Mouepublic string ServiceUrl { get; set; } = default! ;
. I hope Roslyn may have in the future a way to handle Late initialization outside the scope of ctor. I was using MayBe<T> (like Option class), but I switched to nullable Reference Type in c#8 for the new code. – Pystruct
for the Option<T> type allows usingdefault
as a quick way to generateNone
– Moue