It's like someone suddenly noticed they can be called without parenthesis, and when you do so, they look even more like the Where-Object
and ForEach-Object
cmdlets. I don't personally understand the problem with that -- they are, after all, exactly like those cmdlets, except for three things:
- The method is blocking (vs. streaming)
- The where method has some optional additional parameters
- The methods are very fast
Of course, teaching these methods is complicated, because they don't show up in Get-Member, and they're virtually undocumented. In fact, it could be argued that you shouldn't use them or teach them at all. Except, did I mention they're blazingly fast?
PS C:\> (measure-command { (1..100000)|Where{ $_ % 50 -eq 0 } }).ToString()
00:00:01.0668781
PS C:\> (measure-command { (1..100000).Where{ $_ % 50 -eq 0 } }).ToString()
00:00:00.2610790
So, in an attempt to avoid the parenthesis issue, I just wanted to point out that this isn't some sort of special syntax for .Where and .ForEach -- In PowerShell you never need parenthesis to call any method which takes a single scriptblock as it's argument.
This is normal PowerShell syntax for scriptblock extension methods.
# It's true that we can call the magic Where and ForEach functions without parenthesis:
(1..100).Where{ $_ % 5 -eq 0 }
(1..5).ForEach{ $_ * $_ }
# In fact, that syntax extends to ALL METHODS that take a scriptblock as the only (mandatory) parameter:
# Imagine we have an object
$o = [PSCustomObject]@{Name="Joel Bennett"; Alias="Jaykul"}
# And this object has a method which takes a scriptblock:
$o | Add-Member ScriptMethod Greet {
param([ScriptBlock]$code)
$greeting = if($code) {
$code.Invoke($this)
} else {
"Hello"
}
"{0} from {1}" -f $greeting, $this.Alias
}
# You can call this method with parenthesis:
$o.Greet() # returns "Hello from Jaykul"
$o.Greet({"Greetings"}) # returns "Greetings from Jaykul"
# Or without:
$o.Greet{"Hello World"} #returns "Hello World from Jaykul"
P.S. As far as documentation for .Where and .ForEach, personally, I rely on the error messages:
PS C:\> (1..100).where()
Cannot find an overload for ".Where({ expression } [, mode [, numberToReturn]])" and the argument count: "0".
PS C:\> (1..100).foreach()
Cannot find an overload for ".ForEach(expression [, arguments...])" and the argument count: "0"
Was a blog post (https://www.sapien.com/blog/2016/04/22/calling-a-method-without-parentheses/) that included an explanation that it was general and not limited to Where and ForEach.