OldFart
OldFart

Reputation: 2479

Make PowerShell think an object is not enumerable

I've been working on some PowerShell functions to manage objects implemented in an assembly we have created. One of the classes I have been working with implements IEnumerable. Unfortunatly, this causes PowerShell to unroll the object at every opportunity. (I can't change the fact that the class implements IEnumerable.)

I've worked around the problem by creating a PSObject and copying the properties of our custom object to the PSObject, then returning that instead of the custom object. But I'd really rather return our custom object.

Is there some way, presumably using my types.ps1xml file, to hide the GetEnumerator() method of this class from PowerShell (or otherwise tell PowerShell to never unroll it).

Upvotes: 10

Views: 1368

Answers (3)

daedam
daedam

Reputation: 11

This may not directly answer the original question, since it technically isn't returning the custom object itself, but I had a similar problem where I was trying to display the properties of a custom type that implements IEnumerable and thought it was worth sharing. Piping this object into cmdlets like Get-Member and Select-Object always enumerates the contents of the collection. However, using those cmdlets directly and passing the object to the -InputObject parameter produces the expected results. Here, I'm using an ArrayList as an example, but $list can be an instance of your custom collection type:

# Get a reference to a collection instance
$list = [System.Collections.ArrayList]@(1,2,3)

# Display the properties of the collection (in this case, the ArrayList),
# not the items it contains (Int32)
Select-Object -InputObject $list -Property *

# List the members of the ArrayList class, not the contained items
Get-Member -InputObject $list

As other comments have mentioned, you can save the results of Select-Object as a variable, creating a new PSObject that you can use directly without enumerating the contents:

# Wrap the collection in a PSObject
$obj = Select-Object -InputObject $list -Property *

# Properties are displayed without enumerating contents
$obj
$obj | Format-List
$obj | Format-Table

Upvotes: 1

Jaykul
Jaykul

Reputation: 15824

Check out the Write-Output replacement http://poshcode.org/2300 which has a -AsCollection parameter that lets you avoid unrolling. But basically, if you're writing a function that outputs a collection, and you don't want that collection unrolled, you need to use CmdletBinding and PSCmdlet:

function Get-Array {
    [CmdletBinding()]
    Param([Switch]$AsCollection) 

    [String[]]$data = "one","two","three","four"
    if($AsCollection) {
        $PSCmdlet.WriteObject($data,$false)
    } else {
        Write-Output $data
    }
}

If you call that with -AsCollection you'll get very different results, although they'll LOOK THE SAME in the console.

C:\PS> Get-Array 

one
two
three
four

C:\PS> Get-Array -AsCollection

one
two
three
four

C:\PS> Get-Array -AsCollection| % { $_.GetType() }


IsPublic IsSerial Name       BaseType                             
-------- -------- ----       --------                             
True     True     String[]   System.Array                         



C:\PS> Get-Array | % { $_.GetType() }


IsPublic IsSerial Name       BaseType                             
-------- -------- ----       --------                             
True     True     String     System.Object                        
True     True     String     System.Object                        
True     True     String     System.Object                        
True     True     String     System.Object   

Upvotes: 5

Richard
Richard

Reputation: 108995

Wrapping in a PSObject is probably the best way.

You could also explicitly wrap it in another collection—PowerShell only unwraps one level.

Also when writing a cmdlet in C#/VB/... when you call WriteObject use the overload that takes a second parameter: if false then PowerShell will not enumerate the object passed as the first parameter.

Upvotes: 8

Related Questions