[OT] my experience with nullable types (C#)
Python
python at python.com
Wed Apr 30 09:06:20 UTC 2025
On Wednesday, 30 April 2025 at 07:17:37 UTC, Kagamin wrote:
> Finally I had my chance to cope with nullable types in one of
> our C# codebases, and the experience wasn't nice.
That's what happens when you convert an old code base assuming
nullability everywhere. And that's a good thing
>
> I had a prejudice that nullable types give some kind of promise
> that you will have only a few of them, but now that I think
> about it I can't remember anyone making this promise, and
> reality is quick to shatter this prejudice in a very ugly way.
That's simply not true and it's dependent of the nullable
context. By default, the nullable context is set to consider
reference types as not nullable. Probably you are complaining
about uninitialized references.
```
SomeClass c1; // this cannot be null
SomeClass? c2 // this can be null
```
In the first case, the compiler will complain if you dare to not
initialize c1 through flow analysis, and will not bother you to
check for nullability in your code. In the second case the
compiler will let you use c2 uninitialized, but will tap you on
the shoulder if you try to use it without checking for null.
> If you have a nullable type, all code now sees it as nullable,
> and you must insert null checks everywhere, even if you know
> it's not null by that point, and the volume of this null check
> spam is uncomfortably large.
Please read again your own words, "if you have a nullable type,
all code now sees it as nullable".
I find it normal, sometimes the compiler is not smart enough to
determine if a null check is needed or not, but you can help him
assuming the responsability. You can do it bluntly by using the
notnull postfix operator (!) or by decorating your functions with
specific attributes like NotNull or NotNullWhen.
>It's also not very clear what's
> the difference between me spamming null checks everywhere by
> hand and processor doing the same automatically.
> Nullable types are retrofitted in dotnet and all legacy
> interfaces return nullable types, this greatly increases number
> of null checks.
> DTOs are destroyed. How can you even have a DTO with
> nonnullable field? Initialize it to a stub value in default
> constructor?
1. You can use the new `required` keyword.
2. You can use the `init` property setter.
3. You can initialize it in the constructor.
4. You can provide a default value.
5. You can declare it as nullable reference (?).
Depending on your use case, choose the best for you. Irrespective
of the path, the compiler never fails to clearly identify in this
case if you need a null check or not.
>That's 1) NullObject pattern, 2) allocates extra
> garbage. The number of null checks is greatly increased.
> Null check operator has pretty high precedence. If you want to
> null check an expression, you'll need to surround it with
> braces, this is especially annoying with await expression as
> checking the result of await is the best place for null check.
> Exclamation also looks like negation, imagine parsing `if(a! ==
> b)`, this happens unexpectedly often.
I agree that the ?? operator precedence is surprising, but the
compiler will not let you go without a null check if it's not
100% sure that a value is not null. That's the purpose of
nullable references check.
You don't need `a! == b`, you can use directly `a == b`, there
is no null check required here for the comparison to mandate the
use of notnull operator (!)
Not null operator (!) is needed in two cases:
1) When you dereference a nullable reference and the compiler
cannot guarantee 100% that your reference cannot be null:
`someNullable!.SomeProp`
2) When you pass a nullable reference to a function which does
not accept nullable references: `func(someNullable!)`. Again,
only if the compiler is not smart enough to guarantee 100% that
`someNullable` is not null before the call through flow analysis.
More information about the Digitalmars-d
mailing list