Why Does the Is Operator Return False When Given Null

Why does the is operator return false when given null?

This question was the subject of my blog on May 30th 2013. Thanks for the great question!


You're staring at an empty driveway.

Someone asks you "can your driveway hold a Honda Civic?"

Yes. Yes it can.

Someone points you at a second driveway. It is also empty. They ask "Can the current contents of my driveway fit in your driveway?"

Yes, obviously. Both driveways are empty! So clearly the contents of one can fit in the other, because there are no contents of either in the first place.

Someone asks you "Does your driveway contain a Honda Civic?"

No, it does not.

You're thinking that the is operator answers the second question: given this value, does it fit in a variable of that type? Does a null reference fit into a variable of this type? Yes it does.

That is not the question that the is operator answers. The question that the is operator answers is the third question. y is X does not ask "is y a legal value of a variable of type X?" It asks "Is y a valid reference to an object of type X?" Since a null reference is not a valid reference to any object of any type, the answer is "no". That driveway is empty; it doesn't contain a Honda Civic.

Another way to look at it is that y is X answers the question "if I said y as X, would I get a non-null result? If y is null, clearly the answer is no!


To look a little deeper at your question:

One expects that the null value belongs to any reference (or nullable) type

One would be implicitly assuming that a type is a set of values, and that assignment compatibility of a value y with a variable of type X is nothing more nor less than checking whether y is a member of set x.

Though that is an extremely common way of looking at types, that is not the only way of looking at types, and it is not the way that C# looks at types. Null references are members of no type in C#; assignment compatibility is not merely checking a set to see if it contains a value. Just because a null reference is assignment compatible with a variable of reference type X does not mean that null is a member of type X. The "is assignment compatible with" relation and the "is a member of type" relation obviously have a lot of overlap, but they are not identical in the CLR.

If musings about type theory interest you, check out my recent articles on the subject:

What is this thing you call a "type"? Part one

What is this thing you call a "type"? Part Two

Why does = return false when == returns true for null values?

There was a huge debate about this oddity when the feature was originally designed back in C# 2.0. The problem is that C# users are completely used to this being meaningful:

if(someReference == null)

When extending equality to nullable value types, you have the following choices.

  1. Nullable equality is truly lifted. If one or both of the operands is null then the result is neither true, nor false, but null. In this case you can either:

    • a) Make it illegal to have a nullable value type equality in an if statement, because the if statement needs a bool, not a nullable bool. Instead, require everyone to use HasValue if they want to compare to null. This is verbose and irritating.

    • b) Automatically convert null to false. The downside of this is that x==null returns false if x is null, which is confusing and works against people's understanding of null comparisons with reference types.

  2. Nullable equality is not lifted. Nullable equality is either true or false, and comparison to null is a null check. This makes nullable equality inconsistent with nullable inequality.

None of these choices is obviously correct; they all have pros and cons. VBScript chooses 1b, for example. After much debate the C# design team chose #2.

Why does (false || null) return null, while (null || false) returns false?

The || operator in JavaScript doesn't necessarily return true or false. It's exact behavior is this:

If the first operand is truthy, it evaluates to the first operand. Otherwise, it evaluates to the second.

This works as expected given two boolean values, but as you have noticed, you can also use it with any other value. In both of your examples, the first operand is falsey. Thus, both expressions evaluate to the second operand.


Note about one way this is used: The behavior of || is often used to create default arguments:

function foo(optionalArg) {  optionalArg = optionalArg || "default!";  console.log(optionalArg);}
foo("test");foo();

Why is the .NET null conditional operator not returning false when trying to check if a collection has any items?

As others have explained, null does not equal false. Using the null-coalescing operator ?? to get around this should work but your syntax is incorrect. The correct syntax would be something like this:

if ((result?.Pets?.Any() ?? false) == false) { ... }

However, for readability, I would go for something like this:

bool petsFound = result?.Pets?.Any() ?? false;
if (!petsFound) { ... }

Alternatively, you could do it the good old way using a null check and the short-circuiting || operator:

if (result?.Pets is null || !result.Pets.Any()) { ... }

Why is it not possible to use the is operator to discern between bool and Nullablebool?

The reason bool and Nullable<bool> behave the same when passed to your method is because whenever you box a Nullable<T> it doesn't actually box the nullable value, instead it unwraps the value of the nullable and boxes that. If the nullable value is null then you end up with just null, rather than a boxed Nullable<T> where HasValue is false.

If you box a non-null value, it'll just box the Value of the Nullable<T>. So from the perspective of WhatIsIt, the first two calls are literally indistinguishable, because the exact same value is being passed in.

That just leaves the question of why both is checks return true, even though what's passed in, in both cases, is a boxed boolean, and not a Nullable<T>. That's answered by the C# language specs, section 7.10.10:

If T is a nullable type, the result is true if D is the underlying type of T.

In this case this is considering E is T and D is defined earlier as a computed value of E where:

If the type of E is a nullable type, D is the underlying type of that nullable type.

This means that the is operator is specifically defined as treating nullable types as being equivalent to their underlying types, regardless of how you mix and match the actual value being checked and the type you're checking with nullable values and that nullable's underlying type.

Why 0 and null are true and false with equal operator?

When you use > and <, null is converted to the number 0. 0 > 0 and 0 < 0 are both false (that's basic math). When you use == and ===, null is not converted. 0 is not equal to null and hence both are false as well.

More generally speaking: Operators are defined for specific data types and if you pass a value of a different data type that value will be converted to the expected data type first. > and < are defined for strings and numbers but not for null. Hence null is (eventually) converted to a number.

== are a little different ===. While == usually performs type conversion, it doesn't do that if you compare against null. That's simply how the algorithm works.

why does the IS operator enters an IF statement when false?

I made a workaround using the link provided by Wai Ha Lee. I had to alter it though, because testing if an item is MailItem still behaved strangely.

So I copy the items first into a separate list, and make sure only items of type MailItem are in that list.

The only way I got this filtered is by using try...catch, I would still like a better way and I am still curious why the test if (items[i] is MailItem) behaves so strangely.

List<MailItem> ReceivedEmail = new List<MailItem>();
foreach (var testMail in items)
{
try
{
ReceivedEmail.Add((MailItem)testMail);
}
catch (System.Exception ex)
{
;
}
}

After this I can use the list ReceivedEmail without the check on MailItem.

Why does null check fail even though object is explicitly initialized as null

Your == overload is wrong, as you are returning false if a is null, ignoring the fact that b could also be null.

What you need to do is return true if both are null, or if a equals b:

public static bool operator ==(AbstractBaseClass a, AbstractBaseClass b)
{
var isANull = ReferenceEquals(null, a);
var isBNull = ReferenceEquals(null, b)
return (isANull && isBNull) || a?.Equals(b) ?? false;
}

Note: In case a is null but b is not, the .? operator will return null, and the ?? operator will return false.

As RufusL wrote in the comments, there's a shorter eqiuvalent code to get the same results:

public static bool operator ==(AbstractBaseClass a, AbstractBaseClass b)
{
return a?.Equals(b) ?? ReferenceEquals(null, b);
}

if a is null, return true if b is also null. if a is not null, return the result of a.Equals(b).

In case a is not null but b is, your Equals method should return false:

protected bool Equals(AbstractBaseClass other)
{
return other != null
? Equals(this.SomeUniqueProperty, other.SomeUniqueProperty)
: false;
}

Why does nullable DateTime HasValue return false when the DateTime has a value?

Your SELECT clause doesn't appear to have an entry for BILLING_PROCESS_DATE, so even though you're only selecting data for rows which have a non-null date, you're not getting the actual date back in the results.



Related Topics



Leave a reply



Submit