1

我有一个类型为 Person 的对象列表,我想将 Person 记录导出到 excel 表(我正在使用 VB.NET 的专有 excel 组件)。使用带有复选框的表单,用户可以指定应该导出哪些 Person 属性。

我没有一个巨大的 if-then-else 树来检查每个复选框(对应于一个属性)是否已被选中,我有一个数据结构,其中对于 Person 中的每个属性我保留一个布尔值(选中/未选中)和字符串形式的属性名称。然后我使用两个这样的for循环:

For Each p As Person In Persons
    ...
    For Each item As ExportColumnData In ExportColumnTable
        ...
        If item.Checked Then
            ...
            Dim o As Object = CallByName(p, item.PropertyName, CallType.Get, Nothing)
            SaveValueToExcelSheet(o)
            ...
        End If
        ...
    Next
    ...
Next

但是,这不是类型安全的,因为我使用 CallByName 提供 PropertyName 作为字符串。有没有更优雅和类型安全的方法可以实现相同的目标?我需要某种方式(不是字符串)来引用这些 Person 对象的属性。

4

3 回答 3

0

只要内容ExportColumnData正确,您的解决方案就很好。如果这些是在运行时动态计算的,那就没问题了。

否则,或者,您可以执行以下操作:用于Type.GetProperties获取PropertyInfo对象列表。然后,您可以使用这些而不是单纯的字符串来提取循环中的属性值:

Dim o As Object = item.PropertyInfo.GetValue(p, Nothing)
于 2013-07-31T12:44:53.693 回答
0

您说您已经有一个类,您可以在其中存储有关 Person 类的属性的信息。您也可以使用它来存储PropertyInfos。

这是一个例子:

Class Person
    Public Property Name As String
    Public Property Age As Integer
End Class

Class ExportProperty
    Public Property [Property] As PropertyInfo
    Public Property Export As Boolean
End Class

Sub Main()
    '' Create a List(Of ExportProperty) from all public properties of Person
    Dim properties = GetType(Person).GetProperties() _
                                    .Select(Function(p) New ExportProperty With { .[Property] = p}) _
                                    .ToList()

    '' Say we want to export only the Age field                                     
    properties.Single(Function(p) p. [Property].Name = "Age").Export = True

    '' Create a person instance to export
    Dim pers = New Person With { .Name = "FooBar", .Age = 67 }  

    '' Only export the properties with Export = True
    For Each prop in properties.Where(Function(p) p.Export)

        '' Use the PropertyInfo.GetValue-method to get the value of the property
        '' 
        Console.WriteLine(prop.[Property].GetValue(pers, Nothing))
    Next
End Sub
于 2013-07-31T12:46:25.737 回答
0

CallByName函数使用反射来通过字符串名称查找和执行属性获取器,因此从某种意义上说,这是不安全的,因为不会进行编译时检查以确保这些名称的属性确实存在于Person类型中.

不幸的是,没有If/Else大块或类似的东西,没有“安全”的方法可以允许编译时类型检查。如果你想让它在编译时检查,你需要直接在代码中按名称调用属性,如果你这样做,它必须在某种大的条件块中。

你可以做一些事情来最小化或改变丑陋的位置。例如,您可以创建所有Person属性的枚举并向Person类添加一个方法,该方法使用大块返回给定枚举项的属性值Select Case。这将使逻辑可重用,但并没有真正减少丑陋。不仅如此,这样做还会把类型检查的责任放在你的代码上,而不是编译器上。

或者,例如,您可以将每个CheckBox控件的标记设置为一个委托,该委托接受一个Person对象并从给定对象返回该选项的正确属性值Person。然后,在循环中,您可以只调用标记中的委托来检索值。例如,如果您有这样的委托:

Private Delegate Function GetPersonProperty(x As Person) As Object

然后你可以像这样设置Tag控件CheckBox

chkFullName.Tag = New GetPersonProperty(Function(x As Person) x.FullName)
chkAge.Tag = New GetPersonProperty(Function(x As Person) x.Age)

然后,在您的循环中,您可以调用中的委托Tag来获取值,如下所示:

Dim myDelegate As GetPersonProperty = CType(item.Tag, GetPersonProperty)
Dim value As Object = myDelegate.Invoke(p)

但是对于这样一个简单的任务来说,这太复杂了。

最后,如果编译时类型检查真的很重要,我只会硬着头皮做大条件块。如果它不是真的那么重要,我会坚持反射并在代码中放置一些体面的异常处理。

于 2013-07-31T12:47:47.337 回答