4
votes

I have a method, Foo, that accepts a string. It does something if the string is null and some other thing if it's not (null is a valid value). It then returns the same string.

Here is Foo with nullable reference types disabled in C# 8.0:

string Foo(string s)
{
    // Do something with s.
    return s;
}

void Bar()
{
    string s = "S";
    string s2 = Foo(s);

    string n = null;
    string n2 = Foo(n);
}

After I enable nullable reference types, string n = null gives a warning. This makes sense, since string is not nullable anymore. I convert its type to string?:

void Bar()
{
    string s = "S";
    string s2 = Foo(s);

    string? n = null; // X
    string? n2 = Foo(n);
}

And now Foo(n) warns me about Foo's new dislike of nullable strings. This also makes sense - Foo should accept a nullable string since it supports both null and non-null values. I change its parameter and therefore, return types to string?:

string? Foo(string? s)
{
    // Do something with s.
    return s;
}

This time it's string s2 = Foo(s), complaining about Foo returning a string? and me trying to assign it to a string.

Is there a way for me to let the flow analysis understand the fact that when I'm supplying Foo a string (and not a string?), then its return value cannot be null?

2
Is this possible in a single method? No.mjwills
Though in theory it would be possible to write a flow analyzer that tracks "is nullability preserved by this method from its inputs to its output?" in practice that's not usually how it is done.Eric Lippert
I wish code contracts had taken off.Şafak Gür
Yeah me too, but they really fell into an awkward spot. The library and rewriter was sufficiently powerful that we did not feel like we had a strong reason to integrate it into the language, but the user perspective is they were not so compelling that people felt that the costs were worth the benefits if they weren't there "for free" in the language. I have some regrets about how that all played out.Eric Lippert
We are contemplating some attributes to help with such scenarios. This specific scenario would use [NullInNullOut] (name is not finalized). github.com/dotnet/roslyn/issues/26761Julien Couvreur

2 Answers

1
votes

This could make the return value not null when the parameter s is not null.

// using System.Diagnostics.CodeAnalysis;

[return: NotNullIfNotNull("s")]
string? Foo(string? s)
{
    // Do something with s.
    return s;
}
-1
votes

Yes, just have two methods. One that accepts a string? and returns a string?, and another that accepts a string and returns a string. You probably want to implement the former in terms of the latter (check for null, if non-null, call other method, otherwise, handle the null case).

Having two methods is how you ensure that the return type differs based in the input type(s).