【问题标题】:Loop to fill class properties?循环填充类属性?
【发布时间】:2015-09-18 23:01:21
【问题描述】:

假设我有一个具有一堆属性(名字、姓氏、地址、城市等)的类人

我从 Excel 文件(使用优秀的 EPPlus 库)中提取数据以填充这些属性,其中每一列都与 Person 属性相同,顺序相同。

有没有办法遍历 Class Person 中的所有属性来填充它们?

目前我正在这样做:

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

这似乎相当不雅。我可以使用一些循环吗?

【问题讨论】:

  • Cells 集合是否包含映射回属性的值?就像Cells[1, 1] 等于“名字”一样吗?如果是这样,那么是的,您可以使用反射来迭代集合并将它们映射回对象上的属性
  • Cells[1, X] 是列名(在 EEPlus 库中,它实际上看起来像 cells[y, x])。它们水平的顺序与我的类属性垂直的顺序相同。我的属性名称与列名不完全匹配(命名约定等)。我对反射没有任何经验。你能指出我正确的方向吗?
  • 在 MSDN 和 Stack Overflow 上有很多关于如何使用反射的示例。参见例如Type.GetProperties() 方法。但请注意,尤其是在输入数据和类属性名称之间没有精确对应的情况下,这不太可能易于实现。如果您尝试使用反射并遇到问题,请提出一个问题,准确说明您实际尝试过的内容,并明确、具体地解释您遇到的问题。
  • 将 17 个属性硬编码到特定的列号会更快,而且您将保持类型安全,而这会因反射而丢失。我会考虑反思的唯一原因是源文件中列的数量和顺序是否会发生变化。
  • 由于您的列名和属性名不匹配,因此您必须创建一个应用于每个属性的属性来处理属性名到列名的映射,然后编写反射代码。反射解决方案将远不如您目前正在做的“优雅”。您还将在性能上受到重大(相对)打击。正如@DStanley 所提到的,当您完成所有这些操作时,您将完成编写属性的手动映射并停止编写其他内容。

标签: c# class oop properties


【解决方案1】:

这里最大的障碍是如何将列索引自动关联到特定属性的问题。 IE。代码应该如何知道列索引1 对应于FirstName 属性。

可以从您的类的类型中检索PropertyInfo 对象的列表。这些对象可以与您的类的实例一起使用来设置或获取属性值。因此,假设您有某种方法可以将列索引映射到特定属性,从而映射到特定的 PropertyInfo 对象,您可以使用与列相同的索引来排序 PropertyInfo 对象列表(或更可能,列索引减一)。

但反思很慢,坦率地说,可能会令人困惑且难以使用,尤其是对于非专家而言。也很难让反射在速度方面表现良好。

就个人而言,我会坚持你已经拥有的。 IE。显式地为适当的索引编写适当的分配。我要做的一项修改是在代码中定义 const 值,而不是使用文字。例如:

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.
}

您可以考虑在单独的方法中抽象从列到属性的映射,这样至少您只需编写一次代码:

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);
    }
}

或者,您可以使用 Action&lt;Person, string&gt; 代表的数组来抽象分配:

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);
    }
}

恕我直言,以上任何一种方法都比反射更简单、更容易理解。即使您在工作表数据的列名称和您的实际属性名称之间存在精确的一对一对应关系,这也是正确的,并且缺少它,它甚至更正确。 :)

【讨论】:

  • 感谢您提供详尽的提示。我非常喜欢使用 const 值来提高代码可读性的想法
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-06-21
  • 2022-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-09-29
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多