Wednesday, July 29, 2009

A C# Language Quiz

I was writing some code today when I suddenly realized that I didn't how a certain very fundamental part of the C# language would behave.

Here's an example:
using System;

namespace TestNullCastToObject
{
class Program
{
static void Main( string[] args )
{
Test t = null;
object o = t;
if ( o is Test )
Console.WriteLine( "a Null Test is a Test" );
else
Console.WriteLine( "a Null Test is _NOT_ a Test" );

Console.ReadLine();
}
}

class Test
{
public int Id { get; set; }
}
}


If you compile and run that sample what do you think the output will be?

No really, think about it.

Ok, I'll tell you what I thought the output would be. I thought the output would be "a Null Test is a Test".

Ok, now I'm going to tell you what the output is.

"a Null Test is _NOT_ a Test"


Does that surprise you as much as it did me? I think I'm actually happy that it behaves this way, but I'm still surprised.

8 comments:

  1. Curious ...

    If you change it to:

    Test t = new Test();

    What prints?

    What about if you did:

    if(null is Test)

    My guess is that the second will say _NOT_ but I am curious if in your case you are getting into the else because null object values always return false when part of a "is" conditional operation or if you are going into the else because your current variable's type is object and not test (which could be determined at compile time).

    Let me know if you have a chance to run these.

    ReplyDelete
  2. Test t = new Test(); prints "a Null Test is a Test" so the "o is Test" returned true.

    if ( null is Test ) returns false, so you get the _NOT_. Not only that, you get a compiler error "The given expression is never of the provided type."

    The interesting thing is that when I say object o = t, I'm not boxing t and I'm not changing the type because Test inherits from object. I'm just referring to it as a subclass.

    But "x is obj" always seems to return false when x is null, regardless of what type x is, which I didn't know.

    ReplyDelete
  3. That could make sense if the type information is stored in only the actual object at runtime and the variable types are only for a compile time check.

    Interesting.

    ReplyDelete
  4. Actually, I expected exactly what you got. Just because you have declared the type of t doesn't give it a value.

    You are the putting the value of the pointer into another variable... once again which is null.

    You are not testing the "type" of the variable (that is a pointer) you are testing the type of the object it references. Keep in mind this is a reference (pointer) variable.

    BOb

    ReplyDelete
  5. I think in many languages, null is a special non-object value. It boils down to what null really means to the language designer. Is null simply an object which is not yet initialized, or is null nothing, void, and therefore has no type.

    For interesting comparison
    JavaScript:
    var x = null;
    x instanceof String;

    Python:
    x = None
    isinstance(x, str)

    Java:
    String x = null;
    System.out.println(x instanceof String);

    ReplyDelete
  6. @Bob: Thanks! You're right, that's definitely how I should have been thinking about it.

    @Jeff: Part of the reason why I even bothered to get confused by this is because I've been messing around with Powershell Script lately, and null there is represented by a special variable called "$null" as opposed to a language keyword, which I find kind of interesting.

    ReplyDelete
  7. This comment has been removed by the author.

    ReplyDelete
  8. I had commented, but I deleted it after realizing what I said wasn't true... lol. The following snippet proves your point:

    Test t = new Test();
    Test nt = null;
    object o = t;
    object ot = nt;
    Console.WriteLine((o is Test).ToString());
    Console.WriteLine((ot is Test).ToString());

    The output is:
    True
    False

    Very interesting!

    ReplyDelete