tags:

views:

526

answers:

8

C# 3.0 introduced the var keyword. And when compiled, the compiler will insert the right types for you. This means that it will even work on a 2.0 runtime. So far so good. But the other day I found a case where, the var keyword would be replaced with just object and thus not specific enough. Say you have something like:

var data = AdoMD.GetData(...); // GetData returns a DataTable

foreach (var row in data.Rows)
{
     string blah = (string)row[0]; // it fails since it's of type object
}

When I try to use row both IntelliSense and the compiler tells me that it is of type object. data.Rows is of type System.Data.DataRowCollection. The following works:

var data = AdoMD.GetData(...); // GetData returns a DataTable

foreach (DataRow row in data.Rows)
{
     string blah = (string)row[0]; // works since it now has the right type
}

This is not a question about the usage of var, there is a thread for that here.

I'm using Visual Studio 2008 SP1 btw.

Edit: correct code now attached.

+3  A: 

This:

using System;

namespace Test
{
    public class X
    {
        public String Bleh;
    }

    public class Y : X
    {
        public String Blah;
    }

    public static class Program
    {
        public static void Main()
        {
            var y = SomeFunctionThatReturnsY();
            // y. <-- this gives me both Blah and Bleh in the dropdown
        }

        public static Y SomeFunctionThatReturnsY()
        {
            return new Y();
        }
    }
}

works as expected, it gives me intellisense showing both Bleh and Blah, in Visual Studio 2008.

Did you have something like that, or exactly that? There might be other things that trip up intellisense than the code you showed.

To answer the actual question. The only difference between spelling out the type, and using var, is that in some cases you must use var, when you simply don't have the correct type for it. This, if I'm not mistaken, is only relevant when you're using anonymous types.

There is no other difference, and the compiler will pick the same type on the left hand side as it has on the right hand side.

Edit: Thanks to @Telos for pointing out that one difference is that when using var, you must of course give the variable a value at declaration time, since the type of the expression on the right-hand side is used to dictate the type of the variable on the left-hand side. When spelling out the type, you can of course opt to not give it a value.

Lasse V. Karlsen
Actually there's one other difference, which is that var must be initialized on declaration.
Telos
+1  A: 

I tried to reproduce this with the following code in a console application.

class Program
{
    static void Main(string[] args)
    {
        var j = returny();
        j.blah();
    }

    private static y returny()
    {
        return new y();
    }
}

class x
{
}

class y : x
{
    public void blah() { }
}

This works as expected, IntelliSense is correct, and Reflector shows that j is of type y. My guess is that if your experiencing something fishy it's more complex then the simple case here.

I am using Visual Studio 2008 RTM as well.

JoshBerke
+1  A: 

My guess is that somethingThatReturnsY is actually declared to return X - even if it returns Y in practice. I expect that if you declare y as Y y = somethingThatReturnsY(); it will fail to compile.

The rules for var are pretty simple (for the cases where it works - there are various restrictions to stop you doing var x = null; etc.

If you think var really is doing the wrong thing, please post a short but complete program to demonstrate.

Jon Skeet
A: 

I don't believe you've provided enough information in your sample code. I made a small program to try to emulate the behavior you describe:

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
        var y = SomeFunctionThatReturnsY();

        MessageBox.Show(y.bla);

        return;
    }

    private Y SomeFunctionThatReturnsY()
    {
        return new Y();
    }
}

internal class X { }

internal class Y : X
{
    public string bla = "Saw a Y, not an X";
}

But the output clearly shows that var is resolving the Y type, not X.

Are you sure that your function returns a Y as a Y reference, and not as an X reference?

Greg D
+6  A: 

I think I see the problem. DataRowCollection is non-generic and thus the only thing the compiler knows is that contains objects of type Object. If it had been a generic datastructure this would have worked.

Anders Rune Jensen
That has always annoyed me.
Jonathan Allen
A: 

It works as it should. It puts the object type as "object" when the compiler is unable to determine the correct data type.

A: 

When you use the foreach keyword, the DateRowCollection is accessed though the IEnumerable interface. The specific method used to access the DataRow is called Current and returns an object. So the var keyword is looking at the return value of the Current method to infer the type.

Jeff Hornby
A: 

DataTable.Rows returns a DataRowCollection which implements InternalDataCollectionBase, which in turn implements ICollection and IEnumerable. The code

foreach(DataRow row in dt.Rows) { }

casts each item in the DataRowCollection to a DataRow. You can demonstrate this by changing it to

foreach(int row in dt.Rows) { }

which compiles but throws an InvalidCastException.

If DataRowCollection implemented IEnumerable<DataRow> then that would not compile and using var would type each object in the list as a DataRow.

Jamie Ide