Can LINQ be used in PowerShell?

前端 未结 5 1016
我寻月下人不归
我寻月下人不归 2020-12-13 23:43

I am trying to use LINQ in PowerShell. It seems like this should be entirely possible since PowerShell is built on top of the .NET Framework, but I cannot get it to work.

5条回答
  •  庸人自扰
    2020-12-13 23:55

    To complement PetSerAl's helpful answer with a broader answer to match the question's generic title:

    Note: Direct support for LINQ - with syntax comparable to the one in C# - is being discussed for a future version of PowerShell Core in this GitHub issue.

    Using LINQ in PowerShell:

    • You need PowerShell v3 or higher.

    • You cannot call the LINQ extension methods directly on collection instances and instead must invoke the LINQ methods as static methods of the [System.Linq.Enumerable] type to which you pass the input collection as the first argument.

      • Having to do so takes away the fluidity of the LINQ API, because method chaining is no longer an option. Instead, you must nest static calls, in reverse order.

      • E.g., instead of $inputCollection.Where(...).OrderBy(...) you must write [Linq.Enumerable]::OrderBy([Linq.Enumerable]::Where($inputCollection, ...), ...)

    • Helper functions and classes:

      • Some methods, such as .Select(), have parameters that accept generic Func<> delegates (e.g, Func can be created using PowerShell code, via a cast applied to a script block; e.g.:
        [Func[object, bool]] { $Args[0].ToString() -eq 'foo' }

        • The first generic type parameter of Func<> delegates must match the type of the elements of the input collection; keep in mind that PowerShell creates [object[]] arrays by default.
      • Some methods, such as .Contains() and .OrderBy have parameters that accept objects that implement specific interfaces, such as IEqualityComparer and IComparer; additionally, input types may need to implement IEquatable in order for comparisons to work as intended, such as with .Distinct(); all these require compiled classes written, typically, in C# (though you can create them from PowerShell by passing a string with embedded C# code to the Add-Type cmdlet); in PSv5+, however, you may also use custom PowerShell classes, with some limitations.

    • Generic methods:

      • Some LINQ methods themselves are generic and therefore require a type parameter; PowerShell cannot directly call such methods and must use reflection instead; e.g.:

        # Obtain a [string]-instantiated method of OfType.
        $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
        
    • The LINQ methods return a lazy enumerable rather than an actual collection; that is, what is returned isn't the actual data yet, but something that will produce the data when enumerated.

      • In contexts where enumeration is automatically performed, notably in the pipeline, you'll be able to use the enumerable as if it were a collection.

        • However, since the enumerable isn't itself a collection, you cannot get the result count by invoking .Count nor can you index into the iterator; however, you can use member enumeration (extracting the values of a property of the objects being enumerated).
      • If you do need the results as a static array to get the usual collection behavior, wrap the invocation in [Linq.Enumerable]::ToArray(...).

        • Similar methods that return different data structures exist, such as ::ToList().

    For an advanced example, see this answer of mine.
    For an overview of all LINQ methods including examples, see this great article.


    In short: using LINQ from PowerShell is cumbersome and is only worth the effort if any of the following apply:

    • you need advanced query features that PowerShell's cmdlets cannot provide.
    • performance is paramount - see this article.

提交回复
热议问题