Search code examples
powershellpowershell-3.0

A PowerShell Set-Member function and the usage of $_


I have been working on some PowerShell code and attempting to make it as readable as possible (something PowerShell is quite nice at). While we have an Add-Member function and a Get-Member function, there is no related Set-Member function. So I went about creating one for my project. However, in the function itself (as shown below) it is requiring me to use the line:

$_.$NotePropertyName = $NotePropertyValue

to work. However, I think I should be using the line, but it doesn't work:

$InputObject.$NotePropertyName = $NotePropertyValue

Why is it reacting this way?

Function Set-Member
{
    [CmdletBinding(DefaultParameterSetName='Message')]
    param(
        [Parameter(ParameterSetName='Message', Position=0,  ValueFromPipeline=$true)] [object[]]$InputObject,
        [Parameter(ParameterSetName='Message', Mandatory=$true)] [string]$NotePropertyName,
        [Parameter(ParameterSetName='Message', Mandatory=$true)] [string]$NotePropertyValue
    )
    $strInitialValue = $InputObject.($NotePropertyName)  # Get the value of the property FirstName
                                                         # for the current object in the pipe
    $_.$NotePropertyName = $NotePropertyValue
}


$objTest = [PSCustomObject]@ {
    FirstName = "Bob"
    LastName = "White"
}

$objTest | ForEach-Object {
    $_ | Set-Member -NotePropertyName "FirstName" -NotePropertyValue "Joe"
    $_      # Push the object back out the pipe
}

$objTest | ForEach-Object {
    $_ | Set-Member -NotePropertyName "FirstName" -NotePropertyValue "Bobby$($_.FirstName)"
    $_      # Push the object back out the pipe
}

Solution

  • You defined the $InputObject parameter as an array of objects. You should have a for loop in your function to iterate over the array and not treat it as a single object. Or change the type to [object] instead of [object[]].

    Since you use a pipeline to call the function you should use the process block of the function or you will only see the last item in the pipeline processed.

    Function Set-Member
    {
        [CmdletBinding(DefaultParameterSetName='Message')]
        param(
            [Parameter(ParameterSetName='Message', Position=0,  ValueFromPipeline=$true)] [object[]]$InputObject,
            [Parameter(ParameterSetName='Message', Mandatory=$true)] [string]$NotePropertyName,
            [Parameter(ParameterSetName='Message', Mandatory=$true)] [string]$NotePropertyValue
        )
        process
        {
            foreach ($obj in $InputObject)
            {
                $strInitialValue = $obj.($NotePropertyName)  # Get the value of the property FirstName
                                                             # for the current object in the pipe
                $obj.$NotePropertyName = $NotePropertyValue
            }
        }
    }