如何编写通用函数在DataTable任意列查找并返回对应列值?
实现通用DataTable跨类型查找函数
需求:编写通用函数,可在任意DataTable的任意列中查找任意值,返回同一行中另一指定列的对应值;无匹配时返回空,数据无多匹配情况,仅需返回第一个匹配结果。
原问题:现有函数仅支持Integer类型查找列、String类型返回列,无法适配任意数据类型列。虽然能获取列的DataType,但row.Field(Of T)为强类型方法,无法用运行时变量指定泛型参数。
解决方案1:非泛型实现(简单直接)
直接使用DataRow的非泛型Item属性取值,处理DBNull与空值的比较逻辑,无需泛型或反射,适配所有数据类型:
Private Function TableLookup(dtb As DataTable, lookupFieldName As String, lookupFieldValue As Object, returnFieldName As String) As Object ' 参数合法性校验 If dtb Is Nothing OrElse String.IsNullOrEmpty(lookupFieldName) OrElse String.IsNullOrEmpty(returnFieldName) Then Return Nothing End If ' 校验列是否存在 If Not dtb.Columns.Contains(lookupFieldName) OrElse Not dtb.Columns.Contains(returnFieldName) Then Return Nothing End If For Each row As DataRow In dtb.Rows ' 处理查找列的DBNull,转为Nothing Dim currentValue As Object = If(row.IsNull(lookupFieldName), Nothing, row(lookupFieldName)) ' 匹配逻辑:处理空值与非空值的比较 Dim isMatch As Boolean = False If currentValue Is Nothing AndAlso lookupFieldValue Is Nothing Then isMatch = True ElseIf currentValue IsNot Nothing AndAlso currentValue.Equals(lookupFieldValue) Then isMatch = True End If If isMatch Then ' 返回目标列值,同样处理DBNull Return If(row.IsNull(returnFieldName), Nothing, row(returnFieldName)) End If Next ' 无匹配返回空 Return Nothing End Function
调用示例:
' 查找ID=123对应的Name字段值,返回后转为String类型 Dim name As String = CStr(TableLookup(myDataTable, "ID", 123, "Name")) ' 查找Name="Alice"对应的Age字段值,转为Integer类型 Dim age As Integer = CInt(TableLookup(myDataTable, "Name", "Alice", "Age"))
解决方案2:泛型+强类型实现(类型安全)
通过泛型参数指定查找和返回的类型,结合LINQ实现强类型取值,保证类型安全,避免手动转换:
Private Function TableLookup(Of TLookup, TReturn)(dtb As DataTable, lookupFieldName As String, lookupFieldValue As TLookup, returnFieldName As String) As TReturn ' 参数合法性校验 If dtb Is Nothing OrElse String.IsNullOrEmpty(lookupFieldName) OrElse String.IsNullOrEmpty(returnFieldName) Then Return Nothing End If ' 校验列类型与泛型参数匹配 If dtb.Columns(lookupFieldName).DataType <> GetType(TLookup) Then Throw New ArgumentException($"查找列类型与指定的{TLookup.Name}不匹配") End If If dtb.Columns(returnFieldName).DataType <> GetType(TReturn) Then Throw New ArgumentException($"返回列类型与指定的{TReturn.Name}不匹配") End If ' LINQ查找匹配行 Dim matchRow = dtb.AsEnumerable().FirstOrDefault(Function(row) Dim fieldValue = row.Field(Of TLookup)(lookupFieldName) ' 处理值类型、引用类型的匹配,包括DBNull转换后的默认值/空值 If EqualityComparer(Of TLookup).Default.Equals(fieldValue, lookupFieldValue) Then Return True End If ' 处理查找列值为DBNull且查找值为空的情况 If row.IsNull(lookupFieldName) AndAlso lookupFieldValue Is Nothing Then Return True End If Return False End Function) ' 返回结果,处理DBNull转为对应类型的空值/默认值 If matchRow IsNot Nothing Then Return If(matchRow.IsNull(returnFieldName), Nothing, matchRow.Field(Of TReturn)(returnFieldName)) Else Return Nothing End If End Function
调用示例:
' 编译器自动推断泛型类型,返回String类型结果 Dim name = TableLookup(myDataTable, "ID", 123, "Name") ' 显式指定泛型类型,返回Integer类型结果 Dim age = TableLookup(Of String, Integer)(myDataTable, "Name", "Alice", "Age")
原无效代码的问题说明
row.Field(Of T)中的泛型参数T必须是编译时确定的类型,而lookupFieldType是运行时获取的Type变量,无法直接作为泛型参数传入,因此原代码无法编译。上述两种方案分别通过非泛型取值、泛型参数约束解决了这个问题。
内容的提问来源于stack exchange,提问作者Alan O'Brien
相关产品推荐
相关产品推荐

