Search code examples
c#nullreferenceexception

How can I avoid a null warning when reading a line of text from a file, without using a supression character?


I recently created a new Windows Forms project using .NET6 - and got a lot of "null" warnings (such as CS8600: Converting null literal or possible null value to non-nullable type.). From other posts that I've found, I see that I can eliminate the warnings by either Disabling the nullable warnings all together, or adding question marks and exclamation marks in appropriate places. Both of these options seem like they just mask potential problems, and I'm wondering if there is a better solution for either of the following code examples.

String LineText, filename, bin, binname;
String[] codes;

InkFileDialog.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
if (InkFileDialog.ShowDialog() == DialogResult.OK)
{
    StreamReader inkfile = new StreamReader(InkFileDialog.FileName);
    while ((LineText = inkfile.ReadLine()) != null)
    {
    }

In the while statement of this first example, "inkfile.ReadLine()" gives the CS8600 warning (mentioned above).

String LineText, filename, bin, binname;
String[] codes;

InkFileDialog.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
if (InkFileDialog.ShowDialog() == DialogResult.OK)
{
    StreamReader inkfile = new StreamReader(InkFileDialog.FileName);
    if (inkfile.EndOfStream)
    {
        MessageBox.Show("Ink file is empty.");
        return;
    }
    else
    {
        // Read header lines from the file
        while (!(LineText = inkfile.ReadLine()).Contains("RowData"))
        {

In the while statement of this second example, LineText = infile.ReadLine() gives the same CS8600 warning, plus CS8602: Dereference of a possibly null reference.

People mention performing null checks, but I can't figure out a way to accomplish this with the while loops that I have.


Solution

  • You're right to be suspicious of disabling the warnings all together -- these warnings were added to let you know about potential bugs in your code, and disabling them hides that. Null, and by extension the NullReferenceException, has been described by its creator as "his billion dollar mistake". Personally, I even go one step further than the default settings and elevate the warnings to errors so that my code won't build until I've addressed these issues, which makes NullReferenceExceptions (almost) impossible.

    The issue in your first example is that you're assigning a nullable type (the return of ReadLine() to a non-nullable variable LineText. As a result the compiler will assume that all future references to LineText could also be null, and you'll get warnings all the way down, as you've discovered.

    Your second example further demonstrates this, as the compiler has (correctly) identified that your call to .Contains("RowData") could cause a NullReferenceException if the return of ReadLine() is null.

    The important thing here is the compiler is right, and it's warning you about potentional bugs in your code; you should listen to it, and handle those bugs. One way to do this is to apply ? nullability to relevant variables.

    You can also use other language/framework features to "teach" the compiler what is safe. The compiler's concern here is that you may try to access LineText outside of the while loop -- it may be true that inside the loop the value can never be null, but any subsequent code will not have the same guarantee. As a result:

    • If the value of LineText is only going to be accessed inside the loop, you can simplify the code to while (inkfile.ReadLine() is string lineText) { /* code accessing lineText */ } and the compiler will understand that the value of lineText inside the loop will not ever be null.
    • Similarly, while (inkfile.ReadLine() is string lineText && lineText.Contains("RowData")) { /* code accessing lineText */ } will have the same affect for your second example.
    • On the other hand, if the result of LineText will be used outside of the loop, listen to the compiler and apply the ? so it can do its job properly.

    Finally, avoid the use of ! as this is effectively disabling a single warning, and only seems to exist for the instances where the compiler cannot be otherwise convinced that a value cannot be null. These instances are rarer with each release of .NET as new features are added to allow you to make nullable context clear.

    Hope this helps :)