0

This code below:

class B
{
    public bool FinishedDownloading { get; set; } = false;
};

class A
{
    public B DownloadStream { get; set; }
};

A a = new A();

Console.WriteLine(a.DownloadStream?.FinishedDownloading != false);
Console.WriteLine(a.DownloadStream?.FinishedDownloading == true);

Outputs:

True
False

How on earth a.DownloadStream?.FinishedDownloading != false is true when DownloadStream is null and FinishedDownloading is false?!

Let's forget about what happend and focus on the result.

We know this statement a.DownloadStream?.FinishedDownloading != false is true, because it's not false, so in our universe when something is true then it should be true, beacause 1 == 1, right?

So why when we do a.DownloadStream?.FinishedDownloading == true the result is now false?! Madness.

I think I kind of know what's going on here. It's because when DownloadStream is null then FinishedDownloading boolean value is not accessible, thus left side is null, so C# "thinks":

  • a.DownloadStream?.FinishedDownloading != false

    Ok let's check if null value is not equal to boolean "false" value. Hmm, it certainly does not, so let's output true.

  • a.DownloadStream?.FinishedDownloading == true

    Ok let's compare a null value with boolean "true" value. Hmm, that doesn't make any sense. It's not true, so let's output false.

Does anyone know if this is really the case? I find it very confusing and bug prone.

Uwe Keim
  • 39,551
  • 56
  • 175
  • 291
  • 3
    Because if `a.DownLoadStream` is null, you get `null != false`, which is true. – MakePeaceGreatAgain Nov 30 '18 at 12:59
  • 4
    If a.DownloadStream == null then your expression boils down to 'if null == true' which is False – hsmiths Nov 30 '18 at 12:59
  • 1
    _"How on earth a.DownloadStream?.FinishedDownloading != false is true"_ - If you write something like this `if(null != false)`, you will get - `Warning: CS0472 The result of the expression is always 'true' since a value of type 'bool' is never equal to 'null' of type 'bool?'` – SᴇM Nov 30 '18 at 13:02
  • 1
    @BartłomiejStyś You need to have `if(null != false)` in your code in order to see that warning, it is for testing purpose, to understand what's going on. `null` represents value of `DownloadStream` when it is `null`. – SᴇM Nov 30 '18 at 13:06
  • @BartłomiejStyś: use: `if (new bool?() != false){}` because that is equal to your case. You will get the warning: _"The result of the expression is always 'true' since a value of type 'bool' is never equal to 'null' of type 'bool?'"_ – Tim Schmelter Nov 30 '18 at 13:07
  • 1
    I guess you need something like `!a.DownloadStream?.FinishedDownloading ?? false` when your `DownloadStream` is `null`. – SᴇM Nov 30 '18 at 13:16
  • Your last approach makes perfectly sense and is the best way to avoid this issue and to keep the code readable: `bool finished=a.DownloadStream?.FinishedDownloading == true;`. What happens here is that the `==` operator will convert the `bool?` to a `bool` to make this comparison possible. This is called [lifted operators](https://stackoverflow.com/questions/3370110/what-are-lifted-operators). – Tim Schmelter Nov 30 '18 at 13:22

1 Answers1

8

You're forgetting that Nullable<bool> (also known as bool?) and bool are two different types with a different list of possible values.

  • bool can either be true or false
  • bool? can be true, false or null

Since you've never initialized the DownloadStream property, it is therefore null. a.DownloadStream?.FinishedDownloading therefore returns a bool? with value null because ?. propagates the null.

And null != false is true since they are two completely different values.

If you initialize the DownloadStream property, e.g. by doing:

class A
{
    public B DownloadStream { get; set; } = new B();
};

Then you will see that a.DownloadStream?.FinishedDownloading != false will return false, which is what you're expecting.

Tim Schmelter
  • 450,073
  • 74
  • 686
  • 939
Flater
  • 12,908
  • 4
  • 39
  • 62