Huh...this is interesting. If you open up System.Management.Automation.dll
and take a look at the code for PSObject's constructor, you'll see the
problem. If you construct an empty PSObject without passing an object, it
calls CommonInitialization() with a default PSCustomObject (retrieved from a
static field on PSCustomObject), which checks the object that's passed in to
see if it's a PSCustomObject and, if so, sets immediateBaseObjectIsEmpty to
true. The end result is that PSObjects that wrap around PSCustomObjects
actually think they don't have an object at all, which is probably the point
of PSCustomObject. They exist entirely to act as containers for dynamic
properties.
Now we get to the interesting part. There are two ways to retrieve the base
object from a PSObject: the public BaseObject property, and the internal
static Base() method. If you examine these methods in Reflector, you'll see
that they're extremely similar, except for one important difference: if
immediateBaseObjectIsEmpty is true, Base() will return the PSObject itself,
while BaseObject will still return the immediate base object (in this case,
a PSCustomObject), because it never actually checks that flag. The result is
that things that depend directly on BaseObject -- such as, for example,
PSObject.Equals() -- will act based on the custom object, while things that
depend on Base() will be looking at the PSObject itself. The reason the -is
operator returns false when you check against PSCustomObject is that
System.Management.Automation.Parser+ComparisonExpressionNode.Execute() calls
Base() -- you can validate this behaviour by using -is to check if it's a
PSObject instead of a PSCustomObject, which returns true.
On the other hand, when you invoke GetType(), that doesn't call either
BaseObject or Base(). The Members property of PSObject is initialized
directly from the object that was passed to the PSObject, even if it's a
PSCustomObject. Gun to my head, I'd say that's why PSCustomObject exists at
all; so the various mechanisms that examine the base object can have
something to look at. However, the end result is that invoking an instance
method on the resulting object goes against the actual PSCustomObject, while
most of the other mechanisms in PSH will call Base() and therefore go
against the wrapping PSObject.
Hopefully this will help explain why you're seeing what you're seeing. I
don't know if the bug is that GetType() returns PSCustomObject or that
Base() returns the wrapping PSObject, but I'm pretty sure there's a bug in
there somewhere -- this behaviour is extremely confusing.
-- Ryan Milligan
"RichS" <RichS@discussions.microsoft.com> wrote in message
news:4AA3DFE8-B65A-4F02-883C-C5DC8D486216@microsoft.com...
>I would stick with gettype() PSCustomObject is I believe a PowerShell
> wrapper object for the underlying .NET object. gettype() will return the
> underlying type which is what I think you are after
> --
> Richard Siddaway
> Please note that all scripts are supplied "as is" and with no warranty
> Blog: http://richardsiddaway.spaces.live.com/
> PowerShell User Group: http://www.get-psuguk.org.uk
>
>
> "Doug" wrote:
>
>> I do an Import-Csv on a file and get False when using the '-is
>> [PSCustomObject]' operator the first element .
>>
>> Should I stick with using .GetType().Name ?
>>
>> PS > $data = Import-Csv test.csv
>> PS > $data[0] | gm
>>
>>
>> TypeName: System.Management.Automation.PSCustomObject
>>
>> Name MemberType Definition
>> ---- ---------- ----------
>> Equals Method System.Boolean Equals(Object obj)
>> GetHashCode Method System.Int32 GetHashCode()
>> GetType Method System.Type GetType()
>> ToString Method System.String ToString()
>> Age NoteProperty System.String Age=1
>> Name NoteProperty System.String Name=John Doe
>>
>>
>> PS > $data[0] -is [object]
>> True
>> PS > $data[0] -is [System.Management.Automation.PSCustomObject]
>> False
>> PS > $data[0].GetType().Name
>> PSCustomObject
>> PS > $data[0].GetType().Name -eq "PSCustomObject"
>> True