PowerShell 中的 LINQ

LINQ in PowerShell

我正在将应用程序从 C# 转换为 PowerShell。如何从 PowerShell 调用 LINQ?



    [Data.DataTable]$dt = New-Object System.Data.DataTable
    [Data.DataColumn]$column = New-Object System.Data.DataColumn "Id", ([int])
    $dt.Columns.Add($column)
    
    # add data
    [Data.DataRow]$row = $dt.NewRow() # 
    $row["Id"] = 1
    $dt.Rows.Add($row)
    $row = $dt.NewRow() # 
    $row["Id"] = 2
    $dt.Rows.Add($row)
    
    # LINQ in C#: int[] results = dt.AsEnumerable().Select(d => d.Field("Id")).ToArray();
    [int[]]$results = [Linq.Enumerable]::Select($dt,[Func[int,int]]{ $args[0]})
    # Error: Cannot find an overload for "Select" and the argument count: "2"
    Write-Host $results

</pre>
	

注意:有关从 PowerShell 使用 LINQ 的限制的一般信息,请参阅


问题在于 System.Linq.Enumerable.Select() 是一个 泛型 方法,PowerShell 无法为所需的 指定类型输入参数,至少从 PowerShell 7.2 开始。为了让它工作,必须使用 反射,这非常麻烦(见底部)。

但是,您 可以改用方便的 PowerShell 功能:member-access enumeration 允许您直接在 [=44= 上访问感兴趣的 属性 ]collection(可枚举),PowerShell 将 return 每个元素的 属性 值 :

[int[]] $results = $dt.Id  # same as: $dt.Rows.Id
$results # print -> array 1, 2

$dt.Id 实际上等同于:$dt | ForEach-Object { $_.Id }


为了完整起见(不值得为这个用例做),这里是基于反射的 LINQ 方法:

# 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')

# Close the method with the types at hand and invoke it via reflection.
[int[]] $results = $selectMethod.MakeGenericMethod([Data.DataRow], [int]).Invoke(
  # No instance to operate on - the method is static.
  $null,
  # The arguments for the method, as an array.
  (
    [Data.DataRow[]] $dt.Rows, 
    [Func[Data.DataRow,int]] { $args[0].Id }
  )
)

# Output the result.
$results

请注意,上面仅显示了如何实例化通用 .Select() 方法。

为了获得一个 [System.Collections.Generic.IEnumerable`1[System.Data.DataRow]] 实例,使用了一个非惰性 array cast ([System.Data.DataRow[]]) 来代替 System.Linq.Enumerable.AsEnumerable() -使用后者也需要使用基于反射的方法。

从上面可以看出,从 PowerShell 使用 LINQ 非常麻烦,至少从 v7.2 开始是这样 - GitHub issue #2226 建议在未来。

对于使用动态(间接)指定数据类型而不是数据的 LINQ 解决方案的 泛化 - 甚至更麻烦 -输入文字,例如[int],请参阅您的后续问题。