PowerShell 中具有动态指定数据类型的 LINQ

LINQ in PowerShell with dynamically specified data types

响应基于事先已知数据类型字面指定为[int], I was given a

现在我想动态地更改数据类型。可能吗?

使用类型 literals ([...]),这需要指定所有类型 verbatim (按字面名称)。

  • 类型文字支持变量引用;例如[Func[Data.DataRow, $columnType]] 工作 - 它会导致 语法错误 .

要根据动态 确定(间接指定)的类型概括链接的答案,需要进行两项修改:

  • 您必须通过 .MakeArrayType().MakeGenericType() 方法构造(实例化)LINQ 方法调用中涉及的封闭泛型。

  • 您必须使用 -asconditional type conversion operator 将输入对象/转换脚本块 ({ ... }) 转换为这些类型。

# Create a sample data table...
[Data.DataTable] $dt = New-Object System.Data.DataTable
[Data.DataColumn] $column = New-Object System.Data.DataColumn "Id", ([int])
$dt.Columns.Add($column)
# ... and add data.
[Data.DataRow]$row = $dt.NewRow()
$row["Id"] = 1
$dt.Rows.Add($row)
$row = $dt.NewRow() 
$row["Id"] = 2
$dt.Rows.Add($row)

# Using reflection, get the open definition of the relevant overload of the 
# static [Linq.Enumerable]::Select() method.
# ("Open" means: its generic type parameters aren't yet bound, i.e. aren't
# yet instantiated with concrete types.)
$selectMethod = [Linq.Enumerable].GetMethods().Where({ 
  $_.Name -eq 'Select' -and $_.GetParameters()[-1].ParameterType.Name -eq 'Func`2' 
}, 'First')

# Dynamically set the name of the column to use in the projection.
$colName = 'Id'

# Dynamically set the in- and output types to use in the LINQ
# .Select() (projection) operation.
$inType = [Data.DataRow]
$outType = [int]


# Now derive the generic types required for the LINQ .Select() method
# from the types above:

# The array type to serve as the input enumerable.
# Note: As explained in the linked answer, the proper - but more cumbersome -
#       solution would be to use reflection to obtain a closed instance of
#       the generic .AsEnumerable() method.
$inArrayType = $inType.MakeArrayType()

# The type for the 'selector' argument, i.e. the delegate performing
# the transformation of each input object.
$closedFuncType = [Func`2].MakeGenericType($inType, $outType)

# Close the generic .Select() method with the given types
# and invoke it.
[int[]] $results = $selectMethod.MakeGenericMethod($inType, $outType).Invoke(
  # No instance to operate on - the method is static.
  $null,
  # The arguments for the method, as an array.
  # Note the use of the -as operator with the dynamically constructed types.
  (
    ($dt.Rows -as $inArrayType), 
    ({ $args[0].$colName } -as $closedFuncType)
  )
)

# Output the result.
$results

退一步:

如链接答案所示,PowerShell 的 member-access enumeration 可以提供相同的功能,语法 大大简化,无需处理类型明确地:

# Create a sample data table...
[Data.DataTable] $dt = New-Object System.Data.DataTable
[Data.DataColumn] $column = New-Object System.Data.DataColumn "Id", ([int])
$dt.Columns.Add($column)
# ... and add data.
[Data.DataRow]$row = $dt.NewRow()
$row["Id"] = 1
$dt.Rows.Add($row)
$row = $dt.NewRow() 
$row["Id"] = 2
$dt.Rows.Add($row)

# Dynamically set the name of the column to use in the projection.
$colName = 'Id'

# Use member-access enumeration to extract the value of the $colName column
# from all rows.
$dt.$colName  # Same as: $dt.Rows.$colName

您可能想备份并问问自己为什么尝试在 PowerShell 中使用 LINQ。提示是,如果它看起来像 C#,则可能有更好的方法。

我假设您是 PowerShell 的新手,所以我会快速提醒您为什么 LINQ 在 PowerShell 中实际上“更简单”(从技术上讲,它不再是 LINQ,但我认为它看起来像)当与管道相结合。

找个时间在 PowerShell 提示符下尝试 Get-Help *-Object。注意到显示的 cmdlet 了吗? Select-ObjectWhere-ObjectGroup-ObjectSort-Object 做与 LINQ 相同的事情,并且它们的名称符合您的期望。另外,严格来说没有强类型要求。

$data | Where-Object -Property Greeting -Like *howdy* | Select-Object Name,Year,Greeting