LINQ可以在PowerShell中使用吗?

Jas*_*oyd 44 linq powershell

我试图在PowerShell中使用LINQ.看起来这应该是完全可能的,因为PowerShell是建立在.NET Framework之上的,但我无法让它工作.例如,当我尝试以下(人为的)代码时:

$data = 0..10

[System.Linq.Enumerable]::Where($data, { param($x) $x -gt 5 })
Run Code Online (Sandbox Code Playgroud)

我收到以下错误:

无法找到"Where"和参数计数的重载:"2".

不要紧,这可以通过以下方式实现Where-Object.这个问题的关键不是找到在PowerShell中执行此操作的惯用方法.如果我可以使用LINQ,那么PowerShell中的一些任务将更轻松.

use*_*407 45

您的代码的问题是PowerShell无法决定应该强制转换ScriptBlockinstance({ ... })的特定委托类型.因此,它是无法选择的的通用第二个参数类型,具体委托实例化Where方法.并且它也没有明确指定通用参数的语法.要解决此问题,您需要自己将ScriptBlock实例强制转换为正确的委托类型:

$data = 0..10
[System.Linq.Enumerable]::Where($data, [Func[object,bool]]{ param($x) $x -gt 5 })
Run Code Online (Sandbox Code Playgroud)

为什么[Func[object, bool]]工作,但[Func[int, bool]]不是?

因为你$data就是[object[]],不是[int[]]的,因为PowerShell中创建[object[]]默认的阵列; 但是,您可以[int[]]明确地构造实例:

$intdata = [int[]]$data
[System.Linq.Enumerable]::Where($intdata, [Func[int,bool]]{ param($x) $x -gt 5 })
Run Code Online (Sandbox Code Playgroud)


mkl*_*nt0 27

为了补充PetSerAl的有用答案以及更广泛的答案来匹配问题的通用标题:

注意:在此GitHub问题中,针对未来版本的PowerShell Core正在讨论对LINQ的直接支持 - 其语法与C#中的语法相当.

在PowerShell中使用LINQ:

  • 您需要PowerShell v3或更高版本.

  • 您不能直接在集合实例上调用LINQ扩展方法,而是必须将LINQ方法作为[System.Linq.Enumerable]第一个参数传递输入集合类型的静态方法调用.

    • 必须这样做会消除LINQ API的流动性,因为方法链不再是一种选择.相反,您必须以相反的顺序嵌套静态调用.

    • 例如,而不是$inputCollection.Where(...).OrderBy(...)你必须写[Linq.Enumerable]::OrderBy([Linq.Enumerable]::Where($inputCollection, ...), ...)

  • 辅助函数和类:

    • 某些方法(例如.Select(),具有接受通用Func<>委托的参数)(例如,Func<T,TResult>可以使用PowerShell代码,通过应用于脚本块的强制转换来创建;例如:
      [Func[object, bool]] { $Args[0].ToString() -eq 'foo' }

      • Func<>委托的第一个泛型类型参数必须与输入集合的元素类型相匹配; 请记住,PowerShell [object[]]默认创建数组.
    • 一些方法,例如.Contains()并且.OrderBy具有接受实现特定接口的对象的参数,例如IEqualityComparer<T>IComparer<T>; 另外,输入类型可能需要实现IEquatable<T>,以便比较按预期工作,例如.Distinct(); 所有这些都需要编写通常在C#中编写的编译类(尽管您可以通过将带有嵌入式C#代码的字符串传递给Add-Typecmdlet 来从PowerShell创建它们); 但是,在PSv5 +中,您也可以使用自定义PowerShell,但有一些限制.

  • 通用方法:

    • 一些LINQ 方法本身是通用的,因此需要一个类型参数; PowerShell无法直接调用此类方法,必须使用反射; 例如:

      # Obtain a [string]-instantiated method of OfType<T>.
      $ofTypeString = [Linq.Enumerable].GetMethod("OfType").MakeGenericMethod([string])
      
      # Output only [string] elements in the collection.
      # Note how the array must be nested for the method signature to be recognized.
      > $ofTypeString.Invoke($null, (, ('abc', 12, 'def')))
      abc
      def
      
      Run Code Online (Sandbox Code Playgroud)
  • LINQ方法返回一个迭代器,而不是一个实际的集合.

    • 但是,在许多情况下,您将能够像使用集合一样使用迭代器,例如通过管道发送它时.

      • 但是,您无法通过调用获取结果计数,.Count也无法 索引到迭代器; 但是,您可以使用成员枚举.
    • 如果确实需要将结果作为静态数组来获取通常的集合行为,请将调用包装在中[Linq.Enumerable]::ToArray(...).

      • 存在返回不同数据结构的类似方法,例如::ToList().

有关高级示例,请参阅我的这个答案.
有关所有LINQ方法(包括示例)概述,请参阅这篇精彩的文章.


简而言之:使用PowerShell中的LINQ非常麻烦,只有在满足以下任何条件时才值得付出努力:

  • 您需要PowerShell的cmdlet无法提供的高级查询功能.
  • 性能至关重要 - 请参阅此文章.


小智 9

如果你想实现像LINQ功能则PowerShell有一些cmdlet和功能,如:Select-ObjectWhere-ObjectSort-ObjectGroup-Object。它具有适用于大多数LINQ功能的cmdlet,例如投影,限制,排序,分组,分区等。

请参阅链接

有关更多详细信息,此链接可能会有所帮助。