我有一个类型为Person的对象列表,我想将个人记录导出到excel表(我正在使用VB.NET的专有excel组件)。使用带有复选框的表单,用户可以指定应导出哪些Person属性。
我没有一个巨大的if-then-else树,而是检查每个复选框(对应一个属性)是否已被检查,我有一个数据结构,其中对于Person中的每个属性我保留一个布尔值(选中/ unchecked)和作为字符串的属性的名称。然后我使用两个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对象的属性。
答案 0 :(得分:0)
只要ExportColumnData
中的内容正确无误,您的解决方案就可以了。如果这些是在运行时动态计算的,那你没问题。
否则,或者您可以执行以下操作:使用Type.GetProperties
获取PropertyInfo
个对象的列表。然后,您可以使用这些而不仅仅是字符串来提取循环中的属性值:
Dim o As Object = item.PropertyInfo.GetValue(p, Nothing)
答案 1 :(得分:0)
您说您已经有一个类,用于存储有关Person类属性的信息。您也可以使用它来存储PropertyInfo
。
以下是一个例子:
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
答案 2 :(得分:0)
CallByName
函数使用反射来按字符串名称查找和执行属性getter,所以你是正确的,因为没有完成编译时检查以确保属性是不安全的这些名称实际上存在于Person
类型中。
不幸的是,如果没有一个大的If/Else
块或类似的东西,就没有“安全”的方式来实现这一点,这将允许编译时类型检查。如果你想在编译时检查它,你需要直接在代码中按名称调用属性,如果你这样做,它必须在某种大的条件块中。
你可以采取一些措施来减少或改变丑陋的位置。例如,您可以创建所有Person
属性的枚举,并向Person
类添加一个方法,该方法使用大Select Case
块返回给定枚举项的属性值。这将使逻辑可重用,但实际上并不那么难看。不仅如此,而且这样做会将类型检查责任放在代码上,而不是编译器上。
或者,您可以,例如,将每个CheckBox
控件的标记设置为一个委托,该委托接受Person
对象并从给定的{{1}返回该选项的正确属性值对象。然后,在循环中,您可以只调用标记中的委托来检索值。例如,如果您有这样的代表:
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
但对于这么简单的任务来说,这太复杂了。
最后,如果编译时类型检查非常重要,我只需咬紧牙关并制作大条件块。如果 重要,那么我只需坚持反思并在代码中加入一些不错的异常处理。