为了补充PetSerAl的有帮助的回答,以适应问题的通用标题,以下是更广泛的回答:
注:以下内容适用至少到PowerShell 7.3.x版本。关于直接支持LINQ的讨论,其语法与C#中的语法相似,将在未来的PowerShell Core版本中进行,详见GitHub问题#2226。
在PowerShell中使用LINQ:
你需要 PowerShell v3 或更高版本。
你不能直接在集合实例上调用 LINQ 扩展方法,而是必须将 LINQ 方法作为
[System.Linq.Enumerable]
类型的静态方法调用,将输入集合作为第一个参数传递给它。
这样做会削弱 LINQ API 的流畅性,因为方法链不再可行。相反,你必须以相反的顺序嵌套静态调用。
例如,你必须写成
[Linq.Enumerable]::OrderBy([Linq.Enumerable]::Where($inputCollection, ...), ...)
,而不是
$inputCollection.Where(...).OrderBy(...)
。
辅助函数和类:
一些方法(如
.Select()
)具有接受泛型
Func<>
委托的参数。可以使用 PowerShell 代码通过将脚本块应用于转换来创建
Func<T,TResult>
,例如:
[Func[object, bool]] { $Args[0].ToString() -eq 'foo' }
。
Func<>
委托的第一个泛型类型参数必须与输入集合的元素类型匹配;请记住,PowerShell 默认创建
[object[]]
数组。
一些方法(如
.Contains()
和
.OrderBy()
)具有接受实现特定接口的对象的参数,例如
IEqualityComparer<T>
和
IComparer<T>
;此外,输入类型可能需要实现
IEquatable<T>
以使比较按预期工作,例如使用
.Distinct()
;所有这些都需要编写编译类,通常是使用 C#(尽管你可以通过将包含嵌入 C# 代码的字符串传递给
Add-Type
cmdlet 来从 PowerShell 中创建它们);然而,在 PSv5+ 中,你也可以使用自定义
PowerShell 类,但有一些限制。
通用方法:
一些 LINQ 方法本身是泛型的,因此需要一个或多个类型参数。
在 PowerShell (Core) 7.2- 和 Windows PowerShell 中,PowerShell 不能直接调用这些方法,而必须使用反射,因为它只支持推断类型参数,而在这种情况下无法完成;例如:
# 获取 OfType<T> 的 [string] 实例化方法。
$ofTypeString = [Linq.Enumerable].GetMethod("OfType").MakeGenericMethod([string])
# 仅输出集合中的 [string] 元素。
# 注意数组必须嵌套以识别方法签名。
PS> $ofTypeString.Invoke($null, (, ('abc', 12, 'def')))
abc
def
在 PowerShell (Core) 7.3+ 中,你现在可以选择显式指定类型参数(请参阅关于_Calling_Generic_Methods_帮助主题);例如:
# 仅输出集合中的 [string] 元素。
# 注意需要将输入数组括在 (...)
# -> 'abc', 'def'
[Linq.Enumerable]::OfType[string](('abc', 12, 'def'))
LINQ 方法返回的是一个惰性可枚举项,而不是实际的集合;也就是说,返回的不是实际的数据,而是在枚举时产生数据的东西。
在自动执行枚举的上下文中,特别是在管道中,你可以像使用集合一样使用可枚举项。
然而,由于可枚举项本身不是集合,因此无法通过调用
.Count
来获取结果计数,也无法对迭代器进行索引;但是,你可以使用
成员访问枚举(提取正在枚举的对象的属性值)。
如果确实需要将结果作为静态数组获取以获得通常的集合行为,请将调用包装在
[Linq.Enumerable]::ToArray(...)
中。
还存在返回不同数据结构的类似方法,例如
::ToList()
。
要查看一个高级示例,请参见此答案。
要了解所有LINQ方法的概述和示例,请参见这篇精彩的文章。
简而言之:
在PowerShell中使用LINQ很麻烦,只有在以下情况下才值得付出努力:
- 您需要PowerShell的cmdlet无法提供的高级查询功能。
- 性能至关重要-请参阅本文。
Where-Object
的态度,然后又谈论“惯用”的方式,这有些奇怪——Where-Object
是 PowerShell 的事实上的惯用方式。如果你真的想要 LINQ(而不是类似 LINQ 的 cmdlet),我建议你研究二进制 cmdlet;这样你就可以将集合传递给它,并在本应使用 LINQ 的地方使用它(C# 或 VB.NET)。 - carrvo