Fun With PowerShell Objects – Adding Methods to PSCustomObject

Introduction

In the previous installment of this series, I covered the various ways to create objects using the PSCustomObject. We saw how to create it using the New-Object cmdlet, then how to add your custom properties to it using the Add-Member cmdlet.

In this post we’ll learn how to add our own methods to our objects using script blocks. Before we go on, just a quick reminder on vocabulary.

In object oriented terminology, objects have properties and methods. From the perspective of the script writer who uses our object, they only know of the properties and methods we expose. They don’t know, or care, how we implemented them.

From the perspective of us, the authors of the code to create the object, it’s a little different. What the end user calls properties we store in variables. We implement our objects methods using functions.

When we are talking about our code in terms of writing our object, you will frequently see the terms properties and variables used interchangeably. Likewise you’ll see the terms method and function used interchangeably.

For all of the examples we’ll display the code, then (when applicable) under it the result of our code. In this article I’ll be using PowerShell Core, 7.2.1, and VSCode. The examples should work in PowerShell 5.1 in the PowerShell IDE, although they’ve not been tested there.

Additionally, be on the lookout for the backtick ` , PowerShell’s line continuation character, at the end of many lines in the code samples. The blog formatting has a limited width, so using the line continuation character makes the examples much easier to read. My post Fun With PowerShell Pipelined Functions dedicates a section to the line continuation character if you want to learn more.

To run a snippet of code highlight the lines you want to execute, then in VSCode press F8 or in the IDE F5. You can display the contents of any variable by highlighting it and using F8/F5.

Script Blocks

Before we get started, it’s important to understand the concept of a script block. A script block is a piece of code encapsulated within squiggly braces {}. This code is meant to execute as a unit. Here’s an example you’ve probably seen before.

$x = 1
if ($x -eq 1)
{ Write-Host 'Yep its one' }

The code on the last line is the script block. This unit of code, within the squiggly braces, will execute should the if statement evaluates to true.

It’s possible to define your own script blocks, and store them in a variable.

$hw = {
        Clear-Host
        "Hello World"
      }

Here I’ve generated a simple block with two lines. The first clears the display, the second prints Hello World.

Now you have a variable, $hw, holding a script block. How can we use this?

First, we could execute the script block stored in the variable. To do so, we need to precede the name of the variable with an ampersand &. The ampersand is the PowerShell shortcut for execute the code block contained in this variable.

& $hw

Result:

Hello World

Although it’s difficult to show, the display did indeed clear before displaying our text.

Note the space between the & and the variable name is optional, this works too.

&$hw

Personally I think the space makes it a bit easier to read, not to mention it’s easier for the eye to notice the &. Whichever way you go, I suggest picking a format and sticking to it.

There are some interesting ways we can use this concept. Of course we’ll look at using script blocks to add new methods to an object in a moment, but another fun thing you can do is pass a script block into a function.

A quick note, it is generally considered poor practice to have Write-Host statements within a function. Since this is just a simple demo though, we’ll use them just to illustrate our concepts.

function Run-AScriptBlock($block)
{
  Write-Host 'About to run a script block'

  & $block

  Write-Host "Block was run"
}

Run-AScriptBlock $hw

Result:

Hello World
Block was run

The first line to display "About to run…." did execute, but it was wiped out due to the Clear-Host in the script block.

The script block then displayed Hello World, and returned control to the function. The function then displayed "Block was run" then exited.

The PowerShell testing tool, Pester, makes extensive use of this functionality to pass the code blocks being tested into Pester’s unit testing functions.

Your imagination is probably running wild at the various ways you can use this. For example, you could build a string of PowerShell code based on certain conditions, then once the string of code is complete execute it.

At this point you have a nice foundation on the concept of script blocks, so let’s move on to the main topic of this post.

Starting Point

This post picks up where the previous one left off. If you recall, our previous post built a function to generate a custom object of type PSCustomObject. If you need an explanation please go back and read part 1 of this series, but to make it easy for you I’ll reproduce the function needed to create the object we’ll be working with.

function Create-Object ($Schema, $Table, $Comment)
{
  # Build a hash table with the properties
  $properties = [ordered]@{ Schema = $Schema
                            Table = $Table
                            Comment = $Comment
                          }

  # Start by creating an object of type PSObject
  $object = New-Object –TypeName PSObject -Property $properties

  Add-Member -InputObject $object `
             -MemberType AliasProperty `
             -Name 'Description' `
             -Value 'Comment'

  # Return the newly created object
  return $object
}

$myObject = Create-Object -Schema 'MySchema' `
                          -Table 'MyTable' `
                          -Comment 'MyComment'
$myObject

Adding a Method (aka Function) to an Object

With the knowledge above, combined with what we learned in the previous post, it turns out adding a simple function to an object is easy. First, we need to define our function in a script block. This function will combine the schema name and the table name with a period to separate the two.

$block = {
           $st = "$($this.Schema).$($this.Table)"
           return $st
         }

In this example I used string interpolation to build my return string. (For more on string interpolation, see my post Fun With PowerShell Strings.)

Note my use of the $this keyword. Just like with objects generated by the more modern classes in PowerShell 5 and beyond, $this is used to represent the current instance of our custom object. Thus we’ll be getting the schema and table names for the current object, stored in $myObject, and not one in another variable that was instantiated using the same code.

If you aren’t a fan of string interpolation, you could have opted for concatenation with $st = $this.Schema + '.' + $this.Table to generate the return value, but interpolation is the generally accepted best practice for building strings.

Concatenation is generally slower and takes more processing power than interpolation. Granted in this case it is barely measurable, but done over a dataset of millions of rows you might see an impact.

With the schema-table string ($st) built it is then returned.

Now we will use the Add-Member cmdlet to add this function to our custom object. Assuming you have already run the function to create the $myObject variable, shown in the Starting Point section, you can use the following code to achieve our goal.

Add-Member -InputObject $myObject `
           -MemberType ScriptMethod `
           -Name 'SchemaTable' `
           -Value $block

I start by passing in the object I wish to add a member to, our $myObject variable.

Next I indicate what type of member we are adding. ScriptMethod is the value to pass in to MemberType to indicate the data in the variable is a script block and should be manifested as a method.

The Name is next, in other words what we want to name this method. Finally into the Value parameter we pass in the variable holding the script block.

Once you add this new member, you can simply call it using the dot notation, with one important caveat. Because this is a method, you need to add parenthesis to the name of the method.

$myObject.SchemaTable()

Result:

MySchema.MyTable

Using the parenthesis lets PowerShell make the distinction between a property and a method.

Parameters

What’s that? You in the back row raising your hand, what did you say? Parameters? You want to add parameters to your new method?

Well, OK because you asked so nice.

This turns out to be pretty easy. We just need to add a param section within our script block.

$block = {
           param ($DatabaseName)
           $dst = "$DatabaseName.$($this.Schema).$($this.Table)"
           return $dst
         }

On the first line of our script block we use the param keyword, then have a list of our parameters within parenthesis. Here we only have one, $DatabaseName, but we could have more separated by commas.

I then build a string that uses the passed in database name, with the schema and table names already in the object, and finally return it.

Next, I use Add-Member to add this function to our object just like I did with the previous example, the only difference being the name and the variable with our script block. Now we can call it.

$myObject.DatabaseSchemaTable('MyDBName')

Result:

MyDBName.MySchema.MyTable

Conclusion

This post began with an explanation of script blocks. Using this knowledge we added new methods to our existing object. One even allowed us to pass in a parameter.

In in the next post we’ll see how to create custom objects from bits of C# code. Later on we’ll see how to add our own properties and methods to an existing object created by PowerShell.

Well conclude this series by returning to our discussion of PowerShell classes by looking at some advanced features of the built in class construct.

The demos in this series of blog posts were inspired by my Pluralsight course PowerShell 7 Quick Start for Developers on Linux, macOS and Windows, one of many PowerShell courses I have on Pluralsight. All of my courses are linked on my About Me page.

If you don’t have a Pluralsight subscription, just go to my list of courses on Pluralsight . At the top is a Try For Free button you can use to get a free 10 day subscription to Pluralsight, with which you can watch my courses, or any other course on the site.

Advertisement

Fun With PowerShell Objects – PSCustomObject

PSCustomObject Sample

Introduction

Before the holidays I had started a series on classes and objects in PowerShell. If you haven’t read my post Fun With PowerShell Classes – The Basics you should give it a quick read. There are terms that were defined in it that we’ll be using here.

For this post I’ll begin a series on the use of PSCustomObject. Prior to the addition of classes in PowerShell 5.0, this was the technique needed to create your own customized objects. It still has a lot of validity today though, as you can use these techniques to extend the objects other people defined, including those already built into PowerShell.

In addition, understanding the use of PSCustomObject will give you a better understanding of the way classes work.

First let me mention that for all of the examples we’ll display the code, then under it the result of our code when applicable. In this article I’ll be using PowerShell Core, 7.2.1, and VSCode. The examples should work in PowerShell 5.1 in the PowerShell IDE, although they’ve not been tested there.

Second, be on the lookout for the backtick ` , PowerShell’s line continuation character, at the end of many lines in the code samples. The blog formatting has a limited width, so using the line continuation character makes the examples much easier to read. My post Fun With PowerShell Pipelined Functions dedicates a section to the line continuation character if you want to learn more.

To run a snippet of code highlight the lines you want to execute, then in VSCode press F8 or in the IDE F5. You can display the contents of any variable by highlighting it and using F8/F5.

Code to Generate Our PSCustomObject

For these demos, I’m going to wrap the creation of our objects within a function. This is a common technique which allows us to create multiple objects based on a single set of code. Note this isn’t required, you could use these techniques anywhere in your code. I just find placing it in functions makes for greater reuse.

If you need a refresher on PowerShell functions, I’ll refer you back to my posts on functions: Fun With PowerShell Basic Functions; Fun With PowerShell Advanced Functions; and Fun With PowerShell Pipelined Functions.

Let’s take a look at our first example, then break down each line.

function Create-Object ($Schema, $Table, $Comment)
{
  # Build a hash table with the properties
  $properties = [ordered]@{ Schema = $Schema
                            Table = $Table
                            Comment = $Comment
                          }

  # Start by creating an object of type PSObject
  $object = New-Object –TypeName PSObject `
                       -Property $properties

  # Return the newly created object
  return $object
}

Our first line, obviously, defines our basic function. For this example I will be creating an object that holds the schema and table names for a database. It also will allow for a comment, so these are the three parameters that are passed in.

To add a list of properties to our object, we need to use a hash table. I define the hash table with three properties, Schema, Table, and Comment, and assign the parameter variables we passed in for their respective values. And yes, I do have a post on hash tables if you wish to know more about them, Fun With PowerShell Hash Tables.

Next is where the magic occurs. I call the cmdlet New-Object. For its TypeName parameter I use the value of PSObject. This will create a variable of type PSCustomObject. I then pass in my hash table name for the Property parameter.

This is assigned to the variable $object. In the final line I return that value to the calling code.

Before someone points it out, yes I could have made the last line of the function simply New-Object –TypeName PSObject -Property $properties and not assigned it to a variable. As I explain in my previously mentioned posts on functions, not consuming the output within the function returns the value to the code that called it.

Assigning to a value within my function, then returning that value, gives me some additional flexibility. Primarily I could use the Write-Verbose and Write-Debug statements to echo additional debugging information back to the developer (or user). It also allows for easier integration with testing tools, such as Pester.

For more info on Write-Verbose and Write-Debug, see my posts Fun With PowerShell Write-Verbose and Fun With PowerShell Write-Debug.

Creating Our Custom Object

Now that we have the function created, we can call it to create our custom object. We’ll then display its properties.

$myObject = Create-Object -Schema 'MySchema' `
                          -Table 'MyTable' `
                          -Comment 'MyComment'

# Display all properties
$myObject

Result:

Schema   Table   Comment
------   -----   -------
MySchema MyTable MyComment

As you can see, simply running the name of our variable produces a table. Across the top are the names of our properties, below it are the values we passed into the function to use as values.

If we want to prove to ourselves this is indeed a PSCustomObject, we can use the GetType method.

$myObject.GetType()

Result:

IsPublic IsSerial Name            BaseType
-------- -------- ----            --------
True     False    PSCustomObject  System.Object

As you can see in the Name column, it is indeed of type PSCustomObject!

Like any object, we can assign new values by referencing the individual properties.

$myObject.Schema = 'New Schema'
$myObject.Comment = 'New Comment'
$myObject

Result:

Schema     Table   Comment
------     -----   -------
New Schema MyTable New Comment

In the output, you can see the values for Schema and Comment were updated. The original value for the Table property remained unchanged.

Adding Properties One At A Time

There is an alternate method we can use to create a custom object. We can create an empty object, then use the Add-Member cmdlet to add each property individually.

Let’s rewrite our function to use this methodology.

function Create-Object ($Schema, $Table, $Comment)
{
  # Start by creating an object of type PSObject
  $object = New-Object –TypeName PSObject

  # Add-Member by passing in input object
  Add-Member -InputObject $object `
             –MemberType NoteProperty `
             –Name Schema `
             –Value $Schema

  # Alternate syntax, pipe the object as an input to Add-Member
  $object | Add-Member –MemberType NoteProperty `
                       –Name Table `
                       –Value $Table

  $object | Add-Member -MemberType NoteProperty `
                       -Name Comment `
                       -Value $Comment

  return $object
}

The function declaration is the same as in our first example. We then create an empty object through the line $object = New-Object –TypeName PSObject. This object now exists, but has none of our custom properties. Yet!

Now we are going to call the Add-Member cmdlet. Add-Member allows us to add new properties and methods to an existing object.

The first parameter, InputObject, is used to indicate what object we want to work with.

The next parameter is MemberType. There are a variety of types we can add to a custom object. In this case we want to add a text property, so the correct value to use is NoteProperty. We’ll see examples of other member types as we progress through this series.

The next two parameters are pretty self explanatory. The Name is what we want to call this property, and Value is the value we want it to have.

We then move on to add the Table property. In this example I wanted to show the alternative format for using Add-Member. You can take the object you want to add a member to, and pipe it into the Add-Member cmdlet. I’ve done this here for the Table property, as well as for the final property Comment.

Using this function has identical results as the previous example.

$myObject = Create-Object -Schema 'MySchema' `
                          -Table 'MyTable' `
                          -Comment 'MyComment'
$myObject

Result:

Schema   Table   Comment
------   -----   -------
MySchema MyTable MyComment

So why use Add-Member? Clearly the first example where we used a hash table to declare our property list was more compact.

Think about what we did here. We added new properties to an existing object! In this case it was a custom object we created. but this could be any PowerShell object. For example, we could have a list of file system objects returned to us by the Get-ChildItem cmdlet. Using Add-Member we have the ability to add new properties (and methods) to those objects.

We’ll see an example of this very thing in a future blog post, but for now let’s move on to the final section in today’s post.

Creating Property Aliases

If you’ve been in IT for any length of time, you’ve probably encountered the situation where two different groups refer to the same property by two different names. In our example, let’s say the DBA’s refer to the comment property as Comment. They would like to use that property name when they use your script.

Your software developers though don’t think of this as the comment property, instead they call it Description. They complain that having to use the property name "Comment" is causing a lot of coding errors.

This is one of those rare times we can make everyone happy, and we do so through the use of a property alias.

We’ll start with the same $object variable we left off with in the previous example. We’ll then use Add-Member to add a new alias.

Add-Member -InputObject $myObject `
           -MemberType AliasProperty `
           -Name 'Description' `
           -Value 'Comment' `
           -PassThru

Result:

Schema     Table   Comment     Description
------     -----   -------     -----------
New Schema MyTable New Comment New Comment

The first thing to note is the MemberType. Instead of a NoteProperty, we are using the type AliasProperty. This will let the Add-Member cmdlet know we are simply adding an alternate name for an existing property.

The Name parameter is the name for the alias. The Value is the name of the already existing property.

You’ll also note I added a switch to Add-Member, PassThru. In our previous examples, leaving this off ran the code but displayed no output. If want to see the results though, you can add the PassThru switch and the new values will be displayed in the terminal, as you saw in the Result section above.

Our alias added, you can see a new column Description. The value under it is identical to the one for comment.

Let’s change the Description property and see what happens.

$myObject.Description = 'The Description'
$myObject

Result:

Schema   Table   Comment         Description
------   -----   -------         -----------
MySchema MyTable The Description The Description

As you can see, both Comment and Description hold the newly updated value. Your users can now use these property names interchangeably.

We can of course update our original function to add the alias at the time the object is created.

function Create-Object ($Schema, $Table, $Comment)
{
  # Build a hash table with the properties
  $properties = [ordered]@{ Schema = $Schema
                            Table = $Table
                            Comment = $Comment
                          }

  # Start by creating an object of type PSObject
  $object = New-Object –TypeName PSObject `
                       -Property $properties

  Add-Member -InputObject $object `
             -MemberType AliasProperty `
             -Name 'Description' `
             -Value 'Comment'

  # Return the newly created object
  return $object
}

$myObject = Create-Object -Schema 'MySchema' `
                          -Table 'MyTable' `
                          -Comment 'MyComment'
$myObject

Result:

Schema   Table   Comment   Description
------   -----   -------   -----------
MySchema MyTable MyComment MyComment

As you can see, the function generated an object that included our alias. In this example I also combined methods. I used a hash table to define our initial set of properties, then used Add-Member to add the alias. I could have also put the addition of the alias in the second version of the function, where each property was added one at a time.

Conclusion

In this post we learned how to create a basic object using PSCustomObject. We added some properties, and even created an alias for one.

Now you may be asking "what about methods? Object can have methods too!" And you would be correct. In the next post in the series we’ll see how to add custom methods to our object.

In future posts we’ll see how to create custom objects from bits of C# code. Then we’ll see how to add our own properties and methods to an existing object created by PowerShell.

We’ll then return to our discussion of PowerShell classes by looking at some advanced features of the built in PowerShell class type.

The demos in this series of blog posts was inspired by my Pluralsight course PowerShell 7 Quick Start for Developers on Linux, macOS and Windows, one of many PowerShell courses I have on Pluralsight. All of my courses are linked on my About Me page.

If you don’t have a Pluralsight subscription, just go to my list of courses on Pluralsight . At the top is a Try For Free button you can use to get a free 10 day subscription to Pluralsight, with which you can watch my courses, or any other course on the site.