20

I new to C# and have a question regarding the use of "var"

When I use the following code everything works great

foreach(DataGridViewRow row in myGrid.Rows)
{
    if (row.Cells[2].Value.ToString().Contains("51000"))
    {
        row.Cells[0].Value = "X";
    }
}

But when I change DataGridViewRow to var I get and error that states

'object' does not contain definition for 'Cells' and no extension method 'Cells' accepting a first argument of type 'object' could be found (are you missing a using directive or an assembly reference?)

NoWar
  • 36,338
  • 80
  • 323
  • 498
Joaquin
  • 205
  • 2
  • 5
  • it sounds like c# is trying to imply the wrong type for DataGridViewRow, this happens when there are ambigous definitiions for myGrid.Rows –  Apr 15 '15 at 19:47
  • You will have the same issue with `foreach (var row in dt.Rows)` where `dt` is a `DataTable`, but explicitly specifying `foreach (DataRow row in dt.Rows)` works fine – Habib Apr 15 '15 at 19:55

4 Answers4

28

myGrid.Rows is of type DataGridViewRowCollection.

This thing is pretty old, its definition reads:

public class DataGridViewRowCollection : ICollection, IEnumerable, IList

Do you see the non-generic interfaces? This class could implement IList<DataGridViewRow> and then var would just work, but it's legacy.

IEnumerable conveys no information about the item type, and the GetEnumerator function doesn't help here, because it returns an IEnumerator, while it could return an IEnumerator<DataGridViewRow>.

Essentially, the C# compiler looks for a GetEnumerator function which returns an object which has a MoveNext function and a Current property (or an IEnumerable<T>/IEnumerable interface when implemented explicitly). This duck-typing approach is for historical reasons, it existed before generics were introduced into the language. The foreach loop variable will be of the same type than the Current property. And within IEnumerator (the non-generic variant), Current is of type object.

Specifying the type explicitly:

foreach (DataGridViewRow row in myGrid.Rows)

simply casts the return value of Current to a DataGridViewRow, for lack of a better mechanism.

You could also use LINQ to achieve the same effect, if you really want to use that var keyword here:

foreach (var row in myGrid.Rows.Cast<DataGridViewRow>())

This works, because the Enumerable.Cast<T> extension method returns an IEnumerable<T>, which in turn makes use of IEnumerator<T>, and T ends up as the type of the Current property of the enumerator, so the type information is propagated.

I doubt you'll benefit from these details at this point, but you may want to keep this for further reference when you'll learn more about the language. You'd have to learn about extension methods and generic types to grasp this.

Community
  • 1
  • 1
Lucas Trzesniewski
  • 50,214
  • 11
  • 107
  • 158
13

It's because the GridView.Rows property returns a GridViewRowCollection type.

In this case var can't infer from usage that the object will be a DataGridViewRow inside.

Source: GridView.Rows Property

Vitor Canova
  • 3,918
  • 5
  • 31
  • 55
5

DataGridViewRow.Rows is of type DataGridViewRowCollection, which doesn't implement IEnumerable<DataGridViewRow>, only IEnumerable. And object is the best guess which compiler can infer when you don't specify cast to DataGridViewRow for row

ASh
  • 34,632
  • 9
  • 60
  • 82
  • 1
    Correct... but explain why only implementing `IEnumerable` causes type `object` to be inferred... – Eric J. Apr 15 '15 at 19:49
  • 1
    Correct. The asker would expect that in 2015 we would have passed the old non-generic .NET 1 days (.NET 2 and generics came in 2005). But with [`DataGridViewRowCollection`](https://msdn.microsoft.com/en-us/library/system.windows.forms.datagridviewrowcollection.aspx), no. – Jeppe Stig Nielsen Apr 15 '15 at 19:57
1

If you change the DataGridViewRow to Var C# is unsure that there is an array called cells. To fix this you could cast the var as a DataGridViewRow however it is almost always better to use the type if you know it, look online for type safety to get more information.

LiverpoolOwen
  • 806
  • 2
  • 10
  • 25
  • I agree that using the type is always better. I was just tinkering with the how and was wondering why var would not work but I get it now - thanks – Joaquin Apr 15 '15 at 20:17
  • 1
    `var` is type-safe; there's nothing wrong with using it when available. The *compiler* infers the type, so the code is compiled exactly as if you had specified it yourself. It's really just syntactic sugar to save you typing out long class-names (and for anonymous types, of course). – Blorgbeard Apr 16 '15 at 01:15