8

In C# 8.0, suppose I have nullable references enabled with <Nullable>enable</Nullable> in the .csproj. This gives me warnings when I try to use possibly-null references in a non-nonnullable usage, so that's great. I can then turn on <WarningsAsErrors /> and get this strictly enforced as errors rather than warnings.

So I was hopeful with this language construct that I could avoid certain null-checking code. Specifically, I (and many others) often wrote methods like this:

public string Message { get; set; }
public void SetFoo(string message) {
   Message = message ?? throw new ArgumentNullException(nameof(message)); 
   Console.WriteLine(Message.ToUpper())
}

I was hopeful to drop the null check, since the reference is non-nullable, and instead write

public string Message { get; set; }
public void SetFoo(string message) {
   Message = message; 
   Console.WriteLine(Message.ToUpper())
}

This seems to be good, in that a call like myThing.SetFoo(null); generates a compiler warning/error. However, I find that code such as myThing.SetFoo(null!); compiles and runs anyway! So, in order to code defensively, I need to keep the old guard clause anyway in case someone calls it with such misuse of the ! operator, right?

Is there any way to avoid writing code to check for null on non-nullable references? I'd expect that there could be a way to make the compiler infer that it should throw an exception in such cases rather than just ignoring non-nullability, but I don't see it.

Patrick Szalapski
  • 8,738
  • 11
  • 67
  • 129
  • 1
    As you noted, NRTs are compile-time only. The general opinion is that you can elide null-checks on internal interfaces (so long as all your code enables NRTs), but you should keep them on external interfaces – canton7 Jan 16 '20 at 14:18
  • 4
    It's a public method (presumably on a public type), so it can be called from a project without nullable reference types, where that can pass `null`. – CodeCaster Jan 16 '20 at 14:18
  • There is also `MaybeNull`/`AllowNull` attributes, which allow to return a set a possible `null` value to non-nullable reference. You should keep in mind them as well, in addition to null-forgiving operator – Pavel Anikhouski Jan 16 '20 at 14:21
  • 1
    Related: https://stackoverflow.com/questions/54526652/when-to-null-check-arguments-with-nullable-reference-types-enabled – canton7 Jan 16 '20 at 14:29

1 Answers1

10

Can we avoid null reference guard clauses with non-nullable method parameters in C# 8.0?

No, because:

  • You can pass nulls explicitly, using null!, which has its usages, see also this question.
  • External code that is compiled without nullable reference type support can call your code and pass null, without getting warnings or errors in the calling project.
CodeCaster
  • 147,647
  • 23
  • 218
  • 272
  • Thanks. Is there any indication that we could get a "hard non-nullability" option that we could opt-in to, perhaps in C#8.1 or C#9? Where would we vote for such a thing with Microsoft? – Patrick Szalapski Jan 16 '20 at 14:37
  • 1
    There are various discussions on https://github.com/dotnet/csharplang/issues. Please don't open a new issue without reviewing the existing ones -- there are a number of open issues around this – canton7 Jan 16 '20 at 14:41
  • 1
    @Patrick what would you expect this option to do? It is a compile-time feature. Your assemblies can be called from code in other assemblies, even through reflection. You can't control how others call your code. If you want to guard all reference parameters agains null without explicitly writing code that does that, look into Aspect-Oriented Programming (AOP). There are free and paid tools that do this, such as [Fody NullGuard](https://github.com/Fody/NullGuard) and [PostSharp](https://www.postsharp.net/blog/post/Validating-parameters-field-and-properties-in-PostSharp-3). – CodeCaster Jan 16 '20 at 14:49
  • 1
    I'd expect it to throw a ArgumentNullException if a method ever takes in a null references on a non-nullable parameter. Perhaps it could also look for other unallowed nulls, too, and throw the appropriate exception without having to code it. The goal would be to cut off unallowed nulls as early as possible. This strikes me as perhaps the next step of non-nullability in C#. – Patrick Szalapski Jan 16 '20 at 14:56
  • 1
    @PatrickSzalapski Indeed, and there are lots of discussions about *how* best to implement neater null-checks. The LDM team specifically do *not* want NRTs to insert compile-time checks for compatibility reasons. However there are discussions around other syntaxes (e.g. `void Foo(string! thing)` or `void Foo(string thing!)`), and many others. I linked you to the place where these discussions happen, in the open – canton7 Jan 16 '20 at 14:58
  • Great, thanks. I did a few searches on https://github.com/dotnet/csharplang/issues and can't find one that is close, but maybe someone else will find it. – Patrick Szalapski Jan 16 '20 at 15:05
  • 1
    @PatrickSzalapski https://github.com/dotnet/csharplang/issues/2145 is the big one (linked to https://github.com/dotnet/csharplang/issues/2144), also https://github.com/dotnet/csharplang/issues/3096, https://github.com/dotnet/csharplang/issues/2149 – canton7 Jan 16 '20 at 15:13
  • Perfect, thanks for finding that. Added a comment here: https://github.com/dotnet/csharplang/issues/2145#issuecomment-575280778 – Patrick Szalapski Jan 16 '20 at 18:22
  • @PatrickSzalapski That suggestion has been made many times before, and will not be accepted because it creates a dialect of C# – canton7 Jan 17 '20 at 09:18