You're confusing hashtables and custom objects. Just because you can create one from the other doesn't mean they behave the same. Hashtables can be enumerated via GetEnumerator()
, which produces a list of DictionaryEntry
objects with Key
and Value
properties.
PS C:\> $ht = @{item1=1 ;item2=2; item3=3}
PS C:\> $ht
Name Value
---- -----
item2 2
item3 3
item1 1
PS C:\> "$ht"
System.Collections.Hashtable
PS C:\> $ht.GetType().FullName
System.Collections.Hashtable
PS C:\> $ht.GetEnumerator() | % { $_.GetType().FullName }
System.Collections.DictionaryEntry
System.Collections.DictionaryEntry
System.Collections.DictionaryEntry
PS C:\> $ht.GetEnumerator() | % { '{0} = {1}' -f $_.Key, $_.Value }
item2 = 2
item3 = 3
item1 = 1
When you transform the hashtable into a custom object it stops being a hashtable (even though its string representation still looks like a hashtable) and also doesn't behave like one any longer:
PS C:\> $o = [PSCustomObject]$ht
PS C:\> $o
item2 item3 item1
----- ----- -----
2 3 1
PS C:\> "$o"
@{item2=2; item3=3; item1=1}
PS C:\> $o.GetType().FullName
System.Management.Automation.PSCustomObject
PS C:\> $o.GetEnumerator()
Method invocation failed because [System.Management.Automation.PSCustomObject]
does not contain a method named 'GetEnumerator'.
At line:1 char:1
+ $o.GetEnumerator()
+ ~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (GetEnumerator:String) [],
RuntimeException
+ FullyQualifiedErrorId : MethodNotFound
To enumerate the properties of an object use its intrinsic properties:
PS C:\> $o.PSObject.Properties
MemberType : NoteProperty
IsSettable : True
IsGettable : True
Value : 2
TypeNameOfValue : System.Int32
Name : item2
IsInstance : True
MemberType : NoteProperty
IsSettable : True
IsGettable : True
Value : 3
TypeNameOfValue : System.Int32
Name : item3
IsInstance : True
MemberType : NoteProperty
IsSettable : True
IsGettable : True
Value : 1
TypeNameOfValue : System.Int32
Name : item1
IsInstance : True
PS C:\> $o.PSObject.Properties | % { '{0} = {1}' -f $_.Name, $_.Value }
item2 = 2
item3 = 3
item1 = 1
As for why your output strings don't look the way you expect: PowerShell doesn't do complex variable expansions in strings. "$i.Value"
is expanded to the string representation of $i
followed by the string ".Value"
. If you want properties of an object in a string you need to use either string concatenation:
"Name: " + $i.Name + ", Value: " + $i.Value
subexpressions:
"Name: $($i.Name), Value: $($i.Value)"
or the format operator:
"Name: {0}, Value: {1}" -f $i.Name, $i.Value
Simplify your function to something like this:
function Test-Object {
[CmdletBinding()]
Param(
[Parameter(Mandatory=$true)]
[array]$ObjectNames
)
foreach ($o in $ObjectNames) {
foreach ($i in $o.PSObject.Properties) {
Write-Verbose "Name: $($i.Name), Value: $($i.Value)"
}
}
}
and it should do what you expect.