I am working with Powershell 4 under Windows 7 and I have cmdlet defined like this:
Function Transfer-File {
[CmdletBinding()]
Param(
[Parameter(Mandatory=$true)]
[System.Management.Automation.PSCustomObject]
$source,
# other parameters removed for brevity
)
# actual logic does not matter
}
I am calling it like this:
$hasError = Transfer-File -source $source -destination $cfg.Destination -logfile $logFile
$source is obtained by parsing a JSON file and looks like this:
@{Path=some path string here; Pack=False}
Write-Host $source.GetType()
outputs:
System.Management.Automation.PSCustomObject
I obtain the following error:
Cannot convert the "@{Path=some path string here; Pack=False}" value of type "System.Management.Automation.PSCustomObject" to type "System.Management.Automation.PSCustomObject".
In a desperate trial to solve it empirically I replace System.Management.Automation.PSCustomObject
with psobject
and it seems to work fine.
Question: Why does my System.Management.Automation.PSCustomObject
does not seem to fit System.Management.Automation.PSCustomObject
from the cmdlet prototype?
Complete code for object creation:
### Configuration ###
$cfg = New-Object –TypeName PSObject
$cfg | Add-Member -MemberType NoteProperty –Name Sources –Value @()
# get configuration from json file
$jsonContent = Get-Content .\config.json | Out-String
$jsonObj = ConvertFrom-Json $jsonContent
$cfg.Sources = $jsonObj.Sources
Foreach ($source in $cfg.Sources)
{
# Write-Host $source.GetType()
$hasError = Transfer-File -source $source -destination $cfg.Destination -logfile $logFile
}
As @VivekKumarSingh's comment suggests, this is a bug related to the accelerator.
This will not work:
# Error
$y = [System.Management.Automation.PSCustomObject]@{Path='some path string here'; Pack=$False}
And this will not work:
$y = [PSCustomObject]@{Path='some path string here'; Pack=$False}
function t ([System.Management.Automation.PSCustomObject]$x) { $x }
# Error
t $y
This will work:
$y = [PSCustomObject]@{Path='some path string here'; Pack=$False}
function t ([PSCustomObject]$x) { $x }
t $y
However, the above will not type cast anything to a PSCustomObject
, because that type inherits from Object
:
PS> function t ([PSCustomObject]$x) { $x.GetType().FullName }
PS> t 5
System.Int32
PS> t 'asdf'
System.String
PS> t $(Get-ChildItem)
System.Object[]
PS> t $(Get-Item .)
System.IO.DirectoryInfo
This will also work, but I'm not entirely sure if it's 100% equivalent.
function t ([System.Management.Automation.PSObject]$x) { $x }
The reason I'm not sure is because of this weirdness:
PS> $a = New-Object -TypeName System.Management.Automation.PSObject -Property @{Path='some path string here'; Pack=$False}
PS> $a.GetType().FullName
System.Management.Automation.PSCustomObject
PS> $a = New-Object -TypeName System.Management.Automation.PSCustomObject -Property @{Path='some path string here'; Pack=$False}
New-Object : A constructor was not found. Cannot find an appropriate constructor for type System.Management.Automation.PSCustomObject.
PS> $a = [PSCustomObject]@{Path='some path string here'; Pack=$False}
PS> $a.GetType().FullName
System.Management.Automation.PSCustomObject
PS> $a = [System.Management.Automation.PSObject]@{Path='some path string here'; Pack=$False}
PS> $a.GetType().FullName
System.Collections.Hashtable
PS> $a = [PSObject]@{Path='some path string here'; Pack=$False}
PS> $a.GetType().FullName
System.Collections.Hashtable
As far as I know, [PSCustomObject]
and [PSObject]
are both accelerators for System.Management.Automation.PSObject
. System.Management.Automation.PSCustomObject
is just an implementation detail they added to make [PSCustomObject]
possible, but, unfortunately, the way that everything works feels a bit inconsistent. I'm not entirely clear on what the [PSObject]
type accelerator is for, however. It appears to do nothing, but it also certainly exists:
PS> ([System.Management.Automation.Cmdlet]).Assembly.GetType('System.Management.Automation.TypeAccelerators')::Get.ContainsKey('psobject')
True