17

I am playing with the nullable types in c# 8 and I found a problem that is bugging me. Suppose I have a method which takes a nullable parameter. When a parameter is null, I want to throw a specific Exception. But I want the method to be clean and check the parameter somewhere else. The check method throws an exception, so after the method the parameter can not be null. Unfortunately, the compiler does not see that and throws warnings at me. Here's the method:

    public void Foo(string? argument)
    {
        GuardAgainst.Null(argument, nameof(argument));
        string variable = argument; // <-- Warning CS8600  Converting null literal or possible null value to non - nullable type
        var length = argument.Length; //<--Warning CS8602  Dereference of a possibly null reference
    }

Here's the check method:

    public static void Null(string? text, string paramName)
    {
        if (text == null)
            throw new ArgumentNullException(paramName);
    }

Now, I can suppress the warning like this:

#pragma warning disable CS8602
var length = argument.Length;
#pragma warning restore CS8602

but it kind of kills my intention to keep my code clean. So my question is: is there a nicer way to suppress the warnings? Or maybe tell a compiler that from now on the parameter is guaranteed to not be null?

Andreas Koder
  • 357
  • 1
  • 3
  • 9
  • 1
    Why dont you use a simple `if .. != null` statement? – Mateech Apr 23 '20 at 10:03
  • Can you not have `Null` return a non-nullable string and call it as part of the assignment to `variable`? – Damien_The_Unbeliever Apr 23 '20 at 10:06
  • Sure, I thought about it. Then I would have var variable = GuardAgainst.Null(parameter, nameof(parameter)); so I would have to make a variable for every parameter that I have. This is the cleanest solution so far, but I wonder if there's a better option – Andreas Koder Apr 23 '20 at 10:10
  • `This is the cleanest solution so far` < what does this mean? How do you define "clean?" Anyone who wants to understand your code has to examine the implementation of `GuardAgainst` and `.Null()`. While ultimately it depends on the circumstance, some might argue that's not very clean, especially when the alternative is a simple `null` check inside an `if` statement. What does `clean` mean to you in this context? What makes a solution more or less `clean` than any other? – Patrick Tucci Apr 23 '20 at 13:56
  • Please try this out: https://learn.microsoft.com/de-de/dotnet/fundamentals/code-analysis/configuration-files – peter70 Dec 10 '21 at 05:59

3 Answers3

14

This does what you want:

public static void Null<T>([NotNull] T? value, string paramName)
{
    if (value == null)
        throw new ArgumentNullException(paramName);
}

The [NotNull] attribute instructs the analysis that, after calling this method, value will not be null.

This means you don't need the ! operator, which is much cleaner and more natural.

void M(string? argument)
{
    GuardAgainst.Null(argument, nameof(argument));
    string variable = argument; // no warning
    // ...
}

The use of an unconstrained generic type parameter T here means that this approach works for both reference types (such as string) and nullable value types (such as int?).

If you're using .NET 6, you can simplify this even further via CallerArgumentExpressionAttribute as follows:

public static void Null<T>(
    [NotNull] T? value,
    [CallerArgumentExpression(parameterName: "value")] string? paramName = null)
{
    if (value == null)
        throw new ArgumentNullException(paramName);
}

With that, the second argument can be omitted, and the caller can be simplified to:

GuardAgainst.Null(argument);

Think of the ? specifier on a type as meaning two things: 1) the value can be null before the call, and 2) the value can be null afterwards. Another way of writing it is [AllowNull, MaybeNull]. The absence of ? in a nullable context equally means [DisallowNull, NotNull]. In the case of your Null method, we end up with [AllowNull, NotNull] due to the manual specification of NotNull.

Drew Noakes
  • 300,895
  • 165
  • 679
  • 742
  • Thanks Drew, this is really cleaner than the !-operator. I used to work with Resharper before, good to see that I can now use this attribute without it. – Andreas Koder May 22 '20 at 15:10
  • This worked fine for string but doesn't work for Guid, any solution? – Mocas Jul 02 '20 at 15:05
  • Guid is a value type, so cannot be null. – Drew Noakes Jul 02 '20 at 15:21
  • Sure, what I meant is Guid? If passing a Guid? to a method with the [Not Null] to check for null and throw, the code analysis doesn't pick that it has been checked and still complains when assigning to Guid after the method call. – Mocas Jul 03 '20 at 06:31
  • @AndreasKoder sorry forgot to tag you, please read my reply above ^^^ – Mocas Jul 03 '20 at 07:37
  • The thing is that Guid? is different from string?. string? ist still a string, the ? is just an instruction to the compiler that it can be null. But Guid? is in fact Nullable and is a whole different type. You would still need to use the Value property to get the actual Guid. But using this property without cheking it for null also causes a warning, which can be avoided by using the NotNullAttribute. – Andreas Koder Dec 12 '21 at 09:39
  • I've updated the answer to include an approach that works for both nullable reference and value types. I've also included a nice little ergonomic improvement for those using .NET 6. – Drew Noakes Dec 15 '21 at 01:09
  • @BijuKalanjoor you need to include the default value of null in the parameter list, such as in my code. – Drew Noakes Jan 30 '22 at 05:37
  • @DrewNoakes, sorry my bad. I fixed the issue. – Biju Kalanjoor Jan 30 '22 at 06:12
  • The `?` specifier on the parameter does the job. Also, if I add an attribute to the return type using `[return: NotNull]`, the warning is suppressed. However, substituting the `?` with `[AllowNull, NotNull]` on the parameter doesn't really remove the warning, not sure why. – Amal K Feb 21 '23 at 14:15
9

Ok, it looks like there is a really simple solution to it - the ! operator You have to use it once after the guard, and then it considered to be not null:

public void Foo(string? argument)
{
    GuardAgainst.Null(argument, nameof(argument));
    var length = argument!.Length; 
}
Andreas Koder
  • 357
  • 1
  • 3
  • 9
4

Consider this solution with the null-coalescing operator ??

The null-coalescing operator ?? returns the value of its left-hand operand if it isn't null; otherwise, it evaluates the right-hand operand and returns its result. The ?? operator doesn't evaluate its right-hand operand if the left-hand operand evaluates to non-null.

public void Foo(string? argument)
{
    string variable = argument ?? throw new ArgumentNullException(nameof(argument));
    var length = argument.Length;
}

This solution is much cleaner in my opinion. You avoid inspecting GuardAgainst class and .Null() static method implemetation details.

Mikolaj
  • 1,231
  • 1
  • 16
  • 34