PowerShell 中具有动态指定数据类型的 LINQ
LINQ in PowerShell with dynamically specified data types
响应基于事先已知数据类型字面指定为[int]
的, I was given a 。
现在我想动态地更改数据类型。可能吗?
使用类型 literals ([...]
),这需要指定所有类型 verbatim (按字面名称)。
- 类型文字不支持变量引用;例如
[Func[Data.DataRow, $columnType]]
不 工作 - 它会导致 语法错误 .
要根据动态 确定(间接指定)的类型概括链接的答案,需要进行两项修改:
您必须通过 .MakeArrayType()
和 .MakeGenericType()
方法构造(实例化)LINQ 方法调用中涉及的封闭泛型。
您必须使用 -as
、conditional 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-Object
、Where-Object
、Group-Object
和 Sort-Object
做与 LINQ 相同的事情,并且它们的名称符合您的期望。另外,严格来说没有强类型要求。
$data | Where-Object -Property Greeting -Like *howdy* | Select-Object Name,Year,Greeting
响应基于事先已知数据类型字面指定为[int]
的
现在我想动态地更改数据类型。可能吗?
[...]
),这需要指定所有类型 verbatim (按字面名称)。
- 类型文字不支持变量引用;例如
[Func[Data.DataRow, $columnType]]
不 工作 - 它会导致 语法错误 .
要根据动态 确定(间接指定)的类型概括链接的答案,需要进行两项修改:
您必须通过
.MakeArrayType()
和.MakeGenericType()
方法构造(实例化)LINQ 方法调用中涉及的封闭泛型。您必须使用
-as
、conditional 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-Object
、Where-Object
、Group-Object
和 Sort-Object
做与 LINQ 相同的事情,并且它们的名称符合您的期望。另外,严格来说没有强类型要求。
$data | Where-Object -Property Greeting -Like *howdy* | Select-Object Name,Year,Greeting