2

One can call many LINQ methods in PowerShell with this simple notation:

[int[]] $numbers = 1..10000
[Linq.Enumerable]::Sum($numbers)

It is even a relatively simple matter to include a lambda in a call:

[Func[int,int]] $delegate = { $n = $args[0]; if ($n % 3) { $n } else { -$n } }
[Linq.Enumerable]::Sum($numbers, $delegate)

What I am wondering, though, is how to call a generic LINQ method from PowerShell: is it even possible? I found this SO question that seems to indicate one can, but I have not determined how to apply that info to LINQ. (Plus, the fact that that is old information, it is quite possible that with PS version 5 there is a cleaner way to do it.)

So how could one call [Linq.Enumerable]::Cast<T>(...) or [Linq.Enumerable]::OfType<T>(...) properly in PowerShell?

2017.05.10 Update

OK, so based on @Mathias comment, let's stick with MakeGenericMethod. In C#, this incantation works:

var ofTypeForString = typeof(System.Linq.Enumerable).GetMethod("OfType").MakeGenericMethod(typeof(string));
var stuff = new object[] { 1.2, "abc", "def" };
var results = ofTypeForString.Invoke(null, new[] { stuff });

The piece I am still missing is how to translate typeof(System.Linq.Enumerable) to PowerShell. I thought that at least one of these should work but they all return null:

[System.Type]::GetType("System.Linq.Enumerable")
[System.Type]::GetType("Linq.Enumerable")
[System.Type]::GetType("Enumerable")

I am sure I am missing something simple; suggestions?

Community
  • 1
  • 1
Michael Sorens
  • 35,361
  • 26
  • 116
  • 172
  • No, PowerShell (as of version 5) still has no syntax for generic extension methods. The example you found is still the way to go – Mathias R. Jessen May 10 '17 at 08:59
  • *The piece I am still missing is how to translate `typeof(System.Linq.Enumerable)` to PowerShell.* `[Linq.Enumerable]` – user4003407 May 11 '17 at 04:48
  • `Type.GetType(string)` [takes](https://msdn.microsoft.com/en-us/library/w3f99sx1.aspx) "[t]he assembly-qualified name of the type to get. See [AssemblyQualifiedName](https://msdn.microsoft.com/en-us/library/system.type.assemblyqualifiedname.aspx)." As such, you need to call it like `[System.Type]::GetType("System.Linq.Enumerable,System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089").GetMethod("OfType")`. You should also just be able to do `[System.Linq.Enumerable].GetMethod("OfType")`, though. – ejohnson May 15 '17 at 21:35

1 Answers1

3

Yes, both PetSerAl and ejohnson's comments are correct, of course; I just had a mental block for some reason. So here is the complete solution for those who may be interested:

$stringType = "".GetType() # set to your target type
$ofTypeForString =
        [Linq.Enumerable].GetMethod("OfType").MakeGenericMethod($stringType)
$stuff = @("12345", 12, "def")
# The last comma below wraps the array arg $stuff within another array
$ofTypeForString.Invoke($null, (,$stuff)) 
Michael Sorens
  • 35,361
  • 26
  • 116
  • 172
  • Do you know how to do this if the method is overloaded? I'm trying to figure out how to do it for the except() method in Linq.Enumerable but I'm getting an error which says: Exception calling "GetMethod" with "1" argument(s): "Ambiguous match found." since it doesn't know which one I want (The one with 1 or two parameters). – KoolAid Jun 30 '17 at 20:56
  • 1
    For the `Except` method you do not need to treat it as a generic call; you can simply use, e.g. `[int[]] $numbersA = @(0, 2, 4, 5, 8); [int[]] $numbersB = @(5, 2, 7, 1); [Linq.Enumerable]::Except($numbersA, $numbersB);`. I refer you to my just-published comprehensive article that illustrates this and lots more: [High Performance PowerShell with LINQ](https://www.simple-talk.com/dotnet/net-framework/high-performance-powershell-linq/) – Michael Sorens Jul 02 '17 at 01:56