【问题标题】:Pass an arbitrary set of paired parameters, where one of the pair items needs to be an arbitrary datatype传递任意一组成对参数,其中一个成对项需要是任意数据类型
【发布时间】:2022-11-03 18:47:21
【问题描述】:

我有一个函数,我想向其传递任意数量的成对参数(即String 变量和第二个任意类型(可能是StringInteger 等) - 因此我声明了这对是Object。可能有一对或多对这种性质。

因此,我能想到的最明显的结构是Tuple(Of String, Object)

这是功能:

Private Function TableLookup(
        table As DataTable,
        ByVal columnNamesAndKeys As List(Of Tuple(Of String, Object)),
        resultColumnName As String) As Object

    Dim filterExpression As String = ""
    For i = 0 To columnNamesAndKeys.Count
        Dim lookupColumn As String = columnNamesAndKeys(i).Item1
        Dim lookupKey As Object = columnNamesAndKeys(i).Item2
        Dim keyType = lookupKey.GetType()
        If keyType IsNot table.Columns(lookupColumn).DataType Then Return Nothing
        If keyType Is GetType(String) Then
            filterExpression += IIf(Len(filterExpression) > 0, " AND ", "") + $"{lookupColumn} = '{lookupKey}'"
        ElseIf keyType Is GetType(Date) Then
            filterExpression += IIf(Len(filterExpression) > 0, " AND ", "") + $"{lookupColumn} = #{lookupKey:M/dd/yyyy h:mm:ss tt}#"
        Else
            filterExpression += IIf(Len(filterExpression) > 0, " AND ", "") + $"{lookupColumn} = {lookupKey}"
        End If
    Next
    Dim row = table.Select(filterExpression).FirstOrDefault()
    Return If(row Is Nothing, Nothing, row(resultColumnName))
End Function

如此调用(对于单对):

Dim someKey As Integer
Dim someValue = TableLookup(
    dtbSomeTable,
    New List(Of Tuple(Of String, Object))
       From {("SomeKey", DirectCast(someKey, Object)).ToTuple},
    "SomeOtherColumn")

因此(对于多对):

Dim someKey As Integer
Dim someOtherKey As String
Dim someValue = TableLookup(
    dtbSomeTable,
    New List(Of Tuple(Of String, Object))
        From {("SomeKey", DirectCast(someKey, Object)).ToTuple,
              ("SomeOtherKey", DirectCast(someOtherKey, Object)).ToTuple},
    "SomeOtherColumn")

所以这作品- 但每次调用它感觉非常笨拙,必须创建一个 Tuples 的临时列表,然后将每个 TupleDirectCast 声明为 Object 以遵守强类型要求。

该函数的重点是在整个代码中提供一个简单的单行代码,以快速查找具有潜在多个、任意标准的列,但是调用中的所有这些操作使得任何不幸不得不维护的人都难以理解它这个...

是否有一种更智能/更简洁的方法来传递任意一组配对参数,其中一个配对项需要是任意Type

【问题讨论】:

  • IEnumerable(Of T) 有差异,但我不确定它会一直到达一个元组。也就是说,我确信您可以为声明为IEnumerable(Of Object) 的参数提供IEnumerable(Of MyType)。除非您特别需要对列表进行索引访问,否则我建议您优先使用 IEnumerable 而不是 List
  • 与您的问题无关,建议将 IIf 函数的使用替换为 If 三元运算符。我注意到有一个使用 If 运算符从函数返回。
  • 不要使用元组列表。使用字典。
  • 谢谢@jmcilhinney - 我最初的精益是也使用Dictionary,但我想我会遇到类似的问题,声明Dictionary(Of String, Object),然后尝试应用StringInteger作为Value一半,但现在已经测试过了,它工作正常(和所以更干净)
  • 推荐If 而不是IIf。后者是一个库函数,用于弥补该语言的 VBA 遗产版本中缺少前者,甚至我认为 .NET 遗产 VB 的早期版本(因此出于兼容性原因在 .NET 的更高版本中仍然存在) .因为它是一个库函数,所以它不能做短路并且它没有任何类型安全性。

标签: vb.net object tuples parameter-passing


【解决方案1】:

您可以通过将Parameter Array (ParamArray ) 与value tuples 结合使用来实现此目的。这使您可以使用任意数量的参数轻松调用该方法,无需显式列表或数组实例化,既没有值的 New 关键字也没有任何强制转换。请注意,我们必须交换columnNamesAndKeysresultColumnName,因为用于传递columnNamesAndKeysParamArray 必须是该方法的最后一个参数。

使用示例:

Dim result = TableLookup(
    dtbSomeTable,
    "ResultColumn",
    ("id", 12), ("name", "Joe"), ("date", #2022/10/28#), ("someKey", someValue))

适配功能:

Private Function TableLookup(
    table As DataTable,
    resultColumnName As String,
    ParamArray columnNamesAndKeys() As (key As String, value As Object)
) As Object

    Dim filterExpression As String = ""
    For i = 0 To columnNamesAndKeys.Length - 1
        Dim lookupColumn As String = columnNamesAndKeys(i).key
        Dim lookupKey As Object = columnNamesAndKeys(i).value
        Dim keyType = lookupKey.GetType()
        If keyType IsNot table.Columns(lookupColumn).DataType Then Return Nothing
        If filterExpression.Length > 0 Then
            filterExpression += " AND "
        End If
        If keyType Is GetType(String) Then
            filterExpression += $"{lookupColumn} = '{DirectCast(lookupKey, String).Replace("'", "''")}'"
        ElseIf keyType Is GetType(Date) Then
            filterExpression += $"{lookupColumn} = #{lookupKey:yyyy/MM/dd h:mm:ss tt}#"
        Else
            filterExpression += $"{lookupColumn} = {lookupKey}"
        End If
    Next
    Dim row = table.Select(filterExpression).FirstOrDefault()
    Return If(row Is Nothing, Nothing, row(resultColumnName))
End Function

我也做了一些重构。

  • 由于我使用了命名元组,因此您可以按名称访问字段,而不仅仅是Item1 或“Item2”。
  • 有条件地添加 " AND " 部分可以在冗长的 If Then Else ...
  • 我将字符串值中的任何单引号替换为两个单引号。例如,像 "John's Pub" 这样的字符串在 SQL 表示法中变成 'John''s Pub'
  • For i = 0 To columnNamesAndKeys.Count 是错误的。索引从0Count - 1。既然我们现在有一个数组,我们必须使用Length

【讨论】:

  • 这行不通。通用 T 将是单一类型,而不是 OP 需要的多种类型。这个想法很好,可以像这样传递元组:{("SomeKey", someKey), ("SomeOtherKey", someOtherKey)}(好吧,如果我们更改 IEnumerable 而不是 List)但是该方法需要一个 T 作为元组的第二个通用参数。如果您允许 OP 像他最初使用 Objects 一样通过,那么 GetType(T) 始终是 Object 并且这会失败。
  • OP 返回Object 因为我事先不知道返回值的列的数据类型?可以是String,也可以是Integer,也可以是Boolean...
  • 好的,我完全修改了我的答案。
猜你喜欢
  • 2018-10-28
  • 1970-01-01
  • 1970-01-01
  • 2012-04-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-09-24
  • 1970-01-01
相关资源
最近更新 更多