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]
,请参阅您的后续问题。
我正在将应用程序从 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]
,请参阅