14

It seems to me there is really no guarantee that a non-nullable variable won't ever have null. Imagine I have a class that has one property that is not nullable:

public class Foo
{
    public Foo(string test)
    {
        Test = test;
    }
    public string Test {get;set;}
}

Now that might seem like it's now cannot be null. But if we reference this class with another library that does not use nullable context, nothing stops it from sending null in there.

Is that correct or there are some runtime checks as well perhaps that ensure this?

Daniel A. White
  • 187,200
  • 47
  • 362
  • 445
Ilya Chernomordik
  • 27,817
  • 27
  • 121
  • 207

5 Answers5

12

This is what MS says about (https://learn.microsoft.com/en-us/dotnet/csharp/tutorials/upgrade-to-nullable-references#interfaces-with-external-code):

The compiler can't validate all calls to your public APIs, even if your code is compiled with nullable annotation contexts enabled. Furthermore, your libraries may be consumed by projects that have not yet opted into using nullable reference types. Validate inputs to public APIs even though you've declared them as nonnullable types.

Wai Ha Lee
  • 8,598
  • 83
  • 57
  • 92
Dmitri Tsoy
  • 561
  • 6
  • 21
  • 1
    It's a shame that Microsoft removed that section from the link. Thank you for preserving it in your answer. – krillgar Mar 08 '23 at 12:34
3

Even in your own code, if you choose to do so, you can pass null, using the null-forgiving operator. null! is considered to be not-null so far as the compiler's nullability analysis is concerned.

Damien_The_Unbeliever
  • 234,701
  • 27
  • 340
  • 448
2

You are correct, other code which is not using the new feature could assign null to this property, there are no run-time checks it is just complier hints.

You could always do it yourself if you want a runtime check:

public string Test { get; set{ if (value == null) throw new ArgumentNullException() } }

Note that you can guarantee not being null in most of your code, you just need to add guards to your top-level Public API and make sure classes are appropriately sealed etc.

Of course people can still use reflection to f*** your code up, but then its on them

Milney
  • 6,253
  • 2
  • 19
  • 33
  • So this effectively means I can still get a Null Reference Exception even though I do use non-nullable type, right? – Ilya Chernomordik Dec 30 '19 at 12:51
  • Well.... **you** can't in the code you compile, cos you have the hints on... but other peoples code which don't have the hints on, but reference your code - yes they can get a null exception – Milney Dec 30 '19 at 12:52
  • Well, if e.g. an automapper uses your constructor, or something like that, it's still you who will get the exception :) – Ilya Chernomordik Dec 30 '19 at 13:08
  • `Of course people can still use reflection to f*** your code up`, true, true indeed. You definitely can use reflection to do this, *is it recommended*, **no**, do people still do it, yes. – Trevor Dec 30 '19 at 13:24
2

someone can always do

var myFoo = new Foo(null);

May be you can use Domain Driven Design

public class Foo
{
    public Foo(string test)
    {
         if (string.IsNullOrWhiteSpace(test))
             throw new ArgumentNullException(nameof(test));

         Test = test;
    }
    public string Test {get;private set;}
}
huMpty duMpty
  • 14,346
  • 14
  • 60
  • 99
-2

To deal with null checks and also make your code readable, I suggest Null Object Design pattern.

More reading here:

https://www.c-sharpcorner.com/article/null-object-design-pattern/

Basically, it involves creating a new object which is derived from same interface and has null instance.

Example:

public class NullExample : IExample  
{  
    private static NullExample _instance;  
    private NullExample()  
    { }  

    public static NullExample Instance  
    {  
        get {  
            if (_instance == null)  
                return new NullExample();  
            return _instance;  
        }  
    }  

    //do nothing methods  
    public void MethodImplementedByInterface1()  
    { }  

    public void MethodImplementedByInterface1()  
    { }  
}  

Nulls cant be avoided, however they can be checked cleanly.

Gauravsa
  • 6,330
  • 2
  • 21
  • 30
  • Shouldn't the `_instance` member be set to `new NullExample()` on first access of the `Instance` property? The referenced article seems to have the same mistake in its singleton pattern. As written, every access of the `Instance` property will create a new `NullExample` instance instead of reusing the first instance. – Zarepheth Jul 25 '23 at 23:48