IanG on Tap

Ian Griffiths in Weblog Form (RSS 2.0)

Blog Navigation

April (2018)

(1 item)

August (2014)

(1 item)

July (2014)

(5 items)

April (2014)

(1 item)

March (2014)

(1 item)

January (2014)

(2 items)

November (2013)

(2 items)

July (2013)

(4 items)

April (2013)

(1 item)

February (2013)

(6 items)

September (2011)

(2 items)

November (2010)

(4 items)

September (2010)

(1 item)

August (2010)

(4 items)

July (2010)

(2 items)

September (2009)

(1 item)

June (2009)

(1 item)

April (2009)

(1 item)

November (2008)

(1 item)

October (2008)

(1 item)

September (2008)

(1 item)

July (2008)

(1 item)

June (2008)

(1 item)

May (2008)

(2 items)

April (2008)

(2 items)

March (2008)

(5 items)

January (2008)

(3 items)

December (2007)

(1 item)

November (2007)

(1 item)

October (2007)

(1 item)

September (2007)

(3 items)

August (2007)

(1 item)

July (2007)

(1 item)

June (2007)

(2 items)

May (2007)

(8 items)

April (2007)

(2 items)

March (2007)

(7 items)

February (2007)

(2 items)

January (2007)

(2 items)

November (2006)

(1 item)

October (2006)

(2 items)

September (2006)

(1 item)

June (2006)

(2 items)

May (2006)

(4 items)

April (2006)

(1 item)

March (2006)

(5 items)

January (2006)

(1 item)

December (2005)

(3 items)

November (2005)

(2 items)

October (2005)

(2 items)

September (2005)

(8 items)

August (2005)

(7 items)

June (2005)

(3 items)

May (2005)

(7 items)

April (2005)

(6 items)

March (2005)

(1 item)

February (2005)

(2 items)

January (2005)

(5 items)

December (2004)

(5 items)

November (2004)

(7 items)

October (2004)

(3 items)

September (2004)

(7 items)

August (2004)

(16 items)

July (2004)

(10 items)

June (2004)

(27 items)

May (2004)

(15 items)

April (2004)

(15 items)

March (2004)

(13 items)

February (2004)

(16 items)

January (2004)

(15 items)

Blog Home

RSS 2.0

Writing

Programming C# 5.0

Programming WPF

Other Sites

Interact Software

C# 3.0 - Var Isn't Object

Friday 23 September, 2005, 05:28 PM

Since the new C# 3.0 language features were unveiled last week at PDC, there has been a lot of commentary, much of which is untainted by such prosaic concerns as bothering to check the facts before ranting.

A particularly popular target for uninformed complaint is the new var keyword. This lets you declare a variable without saying what the variable's type is. For example:

var ivar = 42;

Many developers of a certain background leap to the wrong conclusion when they see this. (Particularly if they're familiar with the identically spelled but utterly different var keyword in JScript.) Here's a fairly typical reaction from someone on the DOTNET-CX mailing list:

"What's wrong with 'Object'?"

This presupposes, incorrectly, that the code above means the same as this snippet:

object iobj = 42;

In fact they are completely different. For example, given these two declarations, this compiles fine:

ivar += 10;

whereas this:

iobj += 10;

causes a compiler error:

error CS0019: Operator '+=' cannot be applied to operands of type 'object' and 'int'

Likewise, consider these two lines

int i1 = ivar;
int i2 = iobj;

The first line compiles just fine. The second causes this error:

error CS0266: Cannot implicitly convert type 'object' to 'int'. An explicit conversion exists (are you missing a cast?)

And there are things a variable of type object will let us do that a var won't. A particular variable of type object can hold different types over its lifetime. So even though it was initialized with an integer, we can make it hold something else later:

iobj = "Hello";

But try that with the var:

ivar = "world!";

and you get this error:

error CS0029: Cannot implicitly convert type 'string' to 'int'

It looks like compiler is behaving as though the type of ivar is int. And that's exactly what's happening. Let's look at some IL - always a good way to work out what's going on. Consider this method:

static void Main(string[] args)
{
    var ivar = 42;
    object iobj = 42;
}

It compiles to the following IL:

.method private hidebysig static void  Main(string[] args) cil managed
{
  .entrypoint
  // Code size       13 (0xd)
  .maxstack  1
  .locals init ([0] int32 ivar,
           [1] object iobj)
  IL_0000:  nop
  IL_0001:  ldc.i4.s   42
  IL_0003:  stloc.0
  IL_0004:  ldc.i4.s   42
  IL_0006:  box        [mscorlib]System.Int32
  IL_000b:  stloc.1
  IL_000c:  ret
} // end of method Program::Main

Notice that ivar is an int32. (Look at the .locals section.) That's IL for System.Int32, or as we usually say in C#, int. But iobj is of type object. (Aka System.Object, or as we say in C#, object.)

So if you bother to compile up an example (or read the C# 3.0 spec) it's instantly pretty clear that var is very much not the same thing as object.

It's Still Statically Typed

Local variables in C# are statically typed. C# 3.0 doesn't change this. The only thing that var changes is that you don't have to declare the type explicitly - the compiler infers the variable's type. Specifically, the variable's type will be whatever type its initialization expression is. (An uninitialized var will cause a compiler error.) For example, our ivar variable above was initialized with the expression 42. That's an integer literal, so the expression's type is System.Int32, which in turn means that ivar's type is System.Int32. The compiler worked out the type for itself, but it's still statically typed. It's as though we had just declared the variable as an int.

I guess some of the confusion arises from the fact that a lot of people are under the mistaken impression that static typing and manifest typing are the same thing. It's easy to fall into that trap if all you've seen is the C family of languages and dynamic languages like ECMAScript (JScript), Python or Ruby. But if you've ever used ML, you'll be well aware that it's perfectly possible for a language to employ static typing without manifest typing. (Manifest typing is where you have to declare the types of your functions or variables explicitly. By contrast, static typing is where the types of variables and expressions are all determined at compile time, and where type checking is also done at compile time.)

You can see this in action at an interactive ML prompt. Here are a couple of variable declarations (the "-" is the prompt by the way; stuff on lines beginning with "-" has been typed in, the rest is what ML printed out):

- val foo = 42;
val foo = 42 : int
- val bar = 42.0;
val bar = 42.0 : real

Note how ML has shown us the type it has inferred for each variable. C# does pretty much the same thing when you use var. It doesn't print out any messages explicitly telling us that this is what it did of course - there isn't a C# prompt! But it's performing a similar process at compile time.

Incidentally, ML can infer much more interesting stuff, like this:

- fun apply x y = x y;
val apply = fn : ('a -> 'b) -> 'a -> 'b

Here, I've defined a function called apply that takes a couple of parameters. ML has looked at what my apply function does: apply assumes the first parameter (x) is a function and evaluates it by passing in the second parameter (y). ML's type inference system has worked this out and inferred a type for apply that reflects this. If you're not familiar with ML, rather than talk you through ML's type declaration syntax, I'll just write the equivalent C# function declaration:

public static T2 apply<T1, T2>(System.Converter<T1, T2> x, T1 y) ...

C# won't infer function declarations for you like this. I just mention the fact that ML can do it to emphasize the point that you don't need explicit type declarations (manifest typing) to get static typing. (Also, I felt I should point out that ML's type inference is a lot more powerful than C#'s - I didn't want to give a false impression of ML...)

The main point is that C# variables declared with var have a static type, and are subject to exactly the same rules as variables whose type is declared explicitly.

What's It For?

Why add this feature to C#? It offers a couple of benefits, one of which is the real reason, and the other of which is really just a useful side-effect. The useful side effect is that you don't have to specify the type name twice. No more of this tedious stuff:.

Dictionary<string, Control> d = new Dictionary<string, Control>();

Once is now enough:

var d = new Dictionary<string, Control>();

But that's not the real reason. That's just a bonus. The real reason var was added is so that we can declare a strongly typed variable without needing to know the name of the variable's type. This is required in order to enable another new C# 3.0 language feature: anonymous types. You wouldn't be able to declare a variable of an anonymous type if you always had to include the type name as part of the variable declaration. That's the main reason var has been added to the language.

But what are anonymous types, and why are they useful? That would be a topic for another blog entry...

[Update: And var isn't a variant either.]

Copyright © 2002-2024, Interact Software Ltd. Content by Ian Griffiths. Please direct all Web site inquiries to webmaster@interact-sw.co.uk