10

I am trying to iterate over a DataTable and get the values from a particular column. So far I just have the Skeleton of the for loop.

foreach (var row in currentTable.Rows)
{
   var valueAtCurrentRow = row[0];
}

This does not work as I expected. I get an compiler error when trying to do row[0], with the message: "Cannot apply indexing with [] to an expression of type Object". But row should not be an object, it is a DataRow.

To fix this I changed the foreach loop to the following:

foreach (DataRow row in currentTable.Rows)
{
   var valueAtCurrentRow = row[0];
}

Why is this necessary? Why can't C# infer the type of row as it would if I was trying to iterate over a string[] for example?

Michael Hancock
  • 2,673
  • 1
  • 18
  • 37
  • It looks like it's because the ```Rows``` property exposes a ```DataRowCollection```, which is IEnumerable, but not IEnumerable which means that enumerating it is the same as enumerating over a collection of ```object```, which means a cast is necessary. – Matt Feb 08 '18 at 10:09

3 Answers3

19

Why can't C# infer the type of row as it would if I was trying to iterate over a string[] for example?

TL;DR: DataTable predates generics :(

DataTable.Rows is declared to return DataRowCollection, which derives from InternalDataCollectionBase. That implements the non-generic ICollection interface, which means that the compiler can't infer the type of row beyond just object.

This isn't specific to DataTable - any time you have something that only implements IEnumerable rather than IEnumerable<T> (and doesn't have a more specific GetEnumerator method for the compiler to use), the inferred iteration element type is just object. When you specify an explicit type for the variable in the foreach loop, the compiler silently inserts a cast for you.

I'd keep the change exactly as you've got it, but an alternative would be to use the Cast LINQ method:

foreach (var row in currentTable.Rows.Cast<DataRow>())
{
   var valueAtCurrentRow = row[0];
}
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • And that's why `DataTable` is mostly deprecated... when you can use `DataTable` – Camilo Terevinto Feb 08 '18 at 10:09
  • @CamiloTerevinto Unfortunately I am working on a legacy system, I have no choice but to use what is already in place. – Michael Hancock Feb 08 '18 at 10:15
  • @CamiloTerevinto I've never heard of `DataTable` and googling does not reveal anything, either. Can you give me a link of what you are talking about? – Uwe Keim Feb 08 '18 at 10:19
  • @UweKeim Look [here](https://learn.microsoft.com/en-us/dotnet/framework/data/adonet/dataset-datatable-dataview/typed-datasets), it's `DataSet` – Camilo Terevinto Feb 08 '18 at 11:17
  • Before reading this i used the OfType instead of casting using Cast , mostly on DataGridView.Rows or similarities when i needed to iterate – Niklas Mar 08 '18 at 09:21
9

The Rows property of the DataTable is an object of type DataRowCollection.

DataRowCollection itself inherits from InternalDataCollectionBase which implements ICollection and IEnumerable.

The data type contained in these collections isn't any further specified than Object thus it can't be inferred to the DataRow class.

See here for reference.

Adwaenyth
  • 2,020
  • 12
  • 24
-2

Reason for it is Rows is DataRowCollection which is IEnumerable instead of IEnumerable<T>, where T is DataRow.

Matt
  • 1,648
  • 12
  • 22
shrot
  • 145
  • 1
  • 9