Reputation: 369
Let's say I have a Class Person with a bunch of properties (FirstName, LastName, Address, City, etc)
I am pulling data to fill these properties from an Excel file (using the excellent EPPlus library) where each of the columns are identical to the Person properties, in the same order.
Is there a way to iterate through all the properties in Class Person to fill them?
Currently I am doing this:
person.FirstName= worksheet.Cells[2, 1].Text;
person.LastName= worksheet.Cells[2, 2].Text;
person.Address= worksheet.Cells[2, 3].Text;
person.City= worksheet.Cells[2, 4].Text;
person.Age= worksheet.Cells[2, 5].Text;
//repeat for a dozen more properties
This seems rather inelegant. Is there some loop I can use?
Upvotes: 1
Views: 1665
Reputation: 70652
The biggest impediment here is the question of how you would automatically associate your column index to a specific property. I.e. how is the code supposed to know that column index 1
corresponds to the FirstName
property.
It is possible to retrieve a list of PropertyInfo
objects from your class's type. These objects can be used with an instance of your class to set or get property values. So assuming you have some way to map a column index to a specific property, and thus to a specific PropertyInfo
object, you could order the list of PropertyInfo
objects in such a way that you use the same index as for the column (or more likely, the column index minus one).
But reflection is slow and, frankly, can be confusing and difficult to use, especially for non-experts. It is also difficult to get reflection to perform well, speed-wise.
Personally, I would stick to what you have already. I.e. explicitly write the appropriate assignment for the appropriate index. The one modification I'd make is instead of using literals in the code, define const
values. E.g.:
const int firstNameColumn = 1;
const int lastNameColumn = 2;
// etc.
void InitializeFromCell(Worksheet worksheet, int rowIndex)
{
person.FirstName = worksheet.Cells[rowIndex, firstNameColumn].Text;
person.LastName = worksheet.Cells[rowIndex, lastNameColumn].Text;
// etc.
}
You might consider abstracting the mapping from column to property in a separate method, so that at least you only have to write the code once:
void SetIndexedProperty(Person person, int propertyIndex, string value)
{
switch (propertyIndex)
{
case firstNameColumn:
person.FirstName = value;
break;
case lastNameColumn:
person.LastName = value;
break;
// etc.
}
}
void InitializeFromCell(Worksheet worksheet, int rowIndex)
{
for (int columnIndex = 1; columnIndex <= numberOfColumns; columnIndex++)
{
SetIndexedProperty(person, columnIndex, worksheet.Cells[rowIndex, columnIndex].Text);
}
}
Alternatively, you could abstract the assignment using an array of Action<Person, string>
delegates:
static readonly Action<Person, string>[] _propertyIndexers =
{
(person, value) => person.FirstName = value,
(person, value) => person.LastName = value,
// etc.
}
void InitializeFromCell(Worksheet worksheet, int rowIndex)
{
for (int columnIndex = 1; columnIndex <= numberOfColumns; columnIndex++)
{
_propertyIndexers[columnIndex - 1](person, worksheet.Cells[rowIndex, columnIndex].Text);
}
}
IMHO, any of the above would be a simpler, easier-to-understand approach than reflection. This would be true even if you had an exact one-for-one correspondence between the worksheet data's column name and your actual property name, and lacking that, it's even more true. :)
Upvotes: 1