A question came up on the Powershell technet forum asking why an empty System.DirectoryServices.SearchResultCollection was evaluating to $true
. The original post is HERE, but the gist of the question is this:
Why does an empty System.DirectoryServices.SearchResultCollection object evaluate to
$true
when used in an if statement (ie if($SearchResult)), but an empty System.Array and System.Collections.ArrayList both evaulate to$false
?
I was aware that Powershell has some special rules for casting objects, but a search for exactly what those rule are only returned very generic terms for how collections were handled (ie this blog post and this book). Nothing specified exactly which types/interfaces were expanded as collections and which were not.
So I did a little digging and came up with the following…
The reason a SearchResultCollection evaluates to true even when it is empty is because it does not implement the IList interface:
IsPublic IsSerial Name
-------- -------- ----
True False ICollection
True False IEnumerable
True False IDisposable
PS> [System.Array].GetInterfaces()
IsPublic IsSerial Name
-------- -------- ----
True False ICloneable
True False IList
True False ICollection
True False IEnumerable
PS> [System.Collections.ArrayList].GetInterfaces()
IsPublic IsSerial Name
-------- -------- ----
True False IList
True False ICollection
True False IEnumerable
True False ICloneable
As you can see, both System.Array and System.Collections.ArrayList implement IList. This is apparently what Powershell uses to determine if it should "look inside" when it converts the object to a boolean.
You can see (what I think is) the proof of this if you load up Reflector (dotPeek, ILSpy, and JustDecompile are free alternatives):
- Open System.Management.Automation from the GAC
- Expand the System.Management.Automation namespace
- Browse down to LanguagePrimitives
- Browse down to the IsTrue(object obj) method and decompile it
In there you can see the algorithm (which I am assuming is being used in this case) that converts objects to boolean. The basic logic is:
- If it is null, return
$false
. - If it is a boolean, return the boolean.
- If it is a string, return
$false
if it is empty, else return$true
. - If it is a number, return
$false
if it is0
, else return$true
. - If it is a SwitchParameter, call its own
ToBool()
method. - Convert it to an IList:
- If this conversion fails, return
$true
(meaning it was an object that was not null, not any of the "special" things above, and not a list for PS to count). - If it is a list and has
0
elements, return$false
. - If it is a list and has
1
element, return theIsTrue(list[0])
value (ie recurse on the one element and return its value. - If it is a list with more than
1
thing in it, return$true
.
- If this conversion fails, return
As you can see, the Array and ArrayList fall into rules 6.2-6.4 because they implement IList, whereas the SearchResultCollection falls into rule 6.1 because it does not implement IList so the conversion to a list fails, which means it was a plain old non-null object which evaluates to $true
in Powershell.