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.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s