psCustomObject as returned data, odd pipeline errors

126 Views Asked by At

I am attempting to satisfy my curiosity about return type performance. Basically I want to see if there is a meaningful difference between a hash table, a psCustomObject and my own class when returning complex data. But I am having issues with the psCustomObject.

So, I started with

class pxObject {
    [String]$String
    [String]$Failure
    [String]$Error

    pxObject ([String]$string, [String]$failure, [String]$error) {
        $this.String = $string
        $this.Failure = $failure
        $this.Error = $error
    }
}


class pxReturn {
    static [HashTable] Hash ([String]$string, [String]$failure, [String]$error) {
        [HashTable]$returnHash = @{
            String  = $string
            Failure = $failure
            Error   = $error
        }

        return $returnHash
    }
    static [psCustomObject] psObject ([String]$string, [String]$failure, [String]$error) {
        [psCustomObject]$returnObject = [psCustomObject]@{    
            String  = $string
            Failure = $failure
            Error   = $error
        }

        return $returnObject
    }

    static [pxObject] pxObject ([String]$string, [String]$failure, [String]$error) {
        [pxObject]$returnObject = [pxObject]::new($string, $failure, $error)

        return $returnObject
    }
    
}

[Int]$count = 1000
CLS

Write-Host 'Hash ' -NoNewLine
(Measure-Command {
    foreach ($i in 1..$count) {
        [pxReturn]::Hash("String $i", "Failure $i", "Error $i")
    }
}).TotalSeconds
Write-Host 'psObject ' -NoNewLine
(Measure-Command {
    foreach ($i in 1..$count) {
        [pxReturn]::psObject("String $i", "Failure $i", "Error $i")
    }
}).TotalSeconds
Write-Host 'pxObject ' -NoNewLine
(Measure-Command {
    foreach ($i in 1..$count) {
        [pxReturn]::pxObject("String $i", "Failure $i", "Error $i")
    }
}).TotalSeconds

and I get Measure-Command : The given key was not present in the dictionary. at the Measure-Command line for the psObject test. Given that Measure-Command has a code block and that sometimes obscures the real error I revised that code to

Write-Host 'psObject ' -NoNewLine
#(Measure-Command {
    foreach ($i in 1..$count) {
        [pxReturn]::psObject("String $i", "Failure $i", "Error $i")
    }
#}).TotalSeconds

and now I get An error occurred while creating the pipeline. with no line info. This seems simple enough, so I am a bit stumped as to why it is failing. Especially since I copied the psCustomObject code from another project where it works fine. That's why the return data looks like it does, I have successfully used a psCustomObject for this very use case before.

EDIT: The one difference with what I had working and what I am trying to do is that before I was using functions. So I revised the above code to add a function that returns a psCustomObject, like so

function psObjectReturn ([String]$string, [String]$failure, [String]$error) {
    [psCustomObject]$returnObject = [psCustomObject]@{    
        String  = $string
        Failure = $failure
        Error   = $error
    }
    return $returnObject
}

and added the appropriate test

Write-Host 'psObject (function) ' -NoNewLine
(Measure-Command {
    foreach ($i in 1..$count) {
        $test = psObjectReturn "String $i" "Failure $i" "Error $i"
    }
}).TotalSeconds

That works a treat. But I am moving everything to classes so it doesn't really address the major question directly. Though it might if you simply CAN'T use [psCustomObject] as the return type from a class method.

Also, performance is looking like this

Hash                0.01107
psObject (function) 0.0453963
pxObject            0.0245367

Which pretty much suggests Hash Tables are fastest, but not enough to make it mandatory, and I can make a decision between a Hash Table and my own class based on other criteria. But I am sure there is something interesting going on under the hood with the psObject from a class error.

1

There are 1 best solutions below

0
On BEST ANSWER

The problem is your use of psObject as a method name, which conflicts with the hidden, intrinsic .psobject property.

A simplified reproduction of the problem:

class Foo { static [pscustomobject] psobject() { return [pscustomobject] @{ foo = 1 } } }
[Foo]::psobject() # -> An error occurred while creating the pipeline.

Simply choosing a different method name resolves the issue:

class Foo { static [pscustomobject] pscustomobject() { return [pscustomobject] @{ foo = 1 } } }
[Foo]::pscustomobject() # OK