Fun With PowerShell Classes – Overloading

Introduction

This is the next installment in our series of advanced techniques for PowerShell classes. In the previous installment we saw how to implement static properties and methods. In this one, we’ll see how to overload your methods.

We’ll take a deeper look at overloading in a moment, but 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.

Additionally, many of the code samples have lines which end in a backtick `, PowerShell’s line continuation character. 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.

In VSCode you can highlight snippet of code then press F8 to execute it. In the IDE, use F5. You can display the contents of any variable by highlighting it and using F8/F5.

Starting Point

For easy reference, here is our demo class as we left it at the end of the previous post.

class TwittererRedux
{
  # Create a property
  [string]$TwitterHandle

  # Create a property and set a default value
  [string]$Name = 'Robert C. Cain'

  # Static Properties
  static [string] $Version = '2022.01.07.002'

  # Function that returns a string
  [string] TwitterURL()
  {
    $url = "https://twitter.com/$($this.TwitterHandle)"
    return $url
  }

  # Function that has no return value
  [void] OpenTwitter()
  {
    Start-Process $this.TwitterURL()
  }

  # Can launch a twitter page without instantiating the class
  static [void] OpenTwitterPage([string] $TwitterHandle)
  {
    # Note here we cannot call the $this.TwitterUrl function
    # because no object exists (hence no $this)
    $url = "http://twitter.com/$($TwitterHandle)"
    Start-Process $url
  }

}

The Need to Overload a Method

About half way into our class definition we have a method named TwitterURL. This function is pretty simple, it takes the value in the TwitterHandle property, composes the URL to the Twitter site for it, then returns it.

Here is what it would look like in action.

$twit = [TwittererRedux]::new()
$twit.TwitterHandle = 'ArcaneCode'
$twit.TwitterURL()

Result:

http://twitter.com/ArcaneCode

What if we had a lot of handles we wanted to get URLs for. It would be a two step process for each one, first assigning the handle to the TwitterHandle property, then calling the TwitterURL method.

$twit.TwitterHandle = 'ArcaneCode'
$twit.TwitterURL()
$twit.TwitterHandle = 'N4IXT'
$twit.TwitterURL()
$twit.TwitterHandle = 'ArcaneTC'
$twit.TwitterURL()
$twit.TwitterHandle = 'BuckWoodyMSFT'
$twit.TwitterURL()
$twit.TwitterHandle = 'tradney'
$twit.TwitterURL()
$twit.TwitterHandle = 'VKCsh'
$twit.TwitterURL()
$twit.TwitterHandle = 'TechTrainerTime'
$twit.TwitterURL()

Of course these could all be put into an array, or read in from a file and run through a foreach loop, even so it still takes two calls to our $twit object. Wouldn’t it be nice to set the TwitterHandle property and return the URL all in one method call?

But what to call the method? We could go with SetHandleAndGetURL, or perhaps UpdateAndGetTwitter, or even HeyHeresAMethodToSetTheTwitterHandleAndGetTheURL.

But in fact we already have a great name in TwitterURL. Its short and explains just what it does. It’s too bad we can’t use it more than once.

Oh wait, we can!

Implementing an Overloaded Method

To create an overload, create a new function within your class and use the same name. To differentiate, you need to have it accept a different number of parameters. Let’s make this clearer by the example below.

  [string] TwitterURL()
  {
    $url = "https://twitter.com/$($this.TwitterHandle)"
    return $url
  }

  [string] TwitterURL($twitterHandle)
  {
    $this.TwitterHandle = $twitterHandle
    $url = $this.TwitterURL()
    return $url
  }

At the top is our original function. Below it is the overloaded version. In it I pass in a single parameter, the $TwitterHandle. PowerShell can use this to determine which version of the function to call.

If you execute the TwitterURL method with no parameters, the version of the function at the top is executed.

When you call TwitterURL and pass in a parameter, the version of the function at the bottom gets run. In here I first access the TwitterHandle property of the current object (represented by $this) and update it from the parameter $twitterHandle.

Next, I called the original function to get the properly formatted URL from the (newly updated) TwitterHandle property.

I chose to do it this way to demonstrate it is possible to call the original version of a function from its overloaded version. It’s certainly not required though, as the code to format the URL could have been copied from the original function. The downside to this however is if I make a change to one area, I have to make it to both.

Also be aware that setting the TwitterHandle property may be considered a side effect by some. It’s not intuitively obvious this will happen, and some users may think this to be a bug. It’s important then to make sure you document this thoroughly in your documentation.

Let’s see it in action. First, here is the complete class with the new overloaded function added.

class TwittererRedux
{
  # Create a property
  [string]$TwitterHandle

  # Create a property and set a default value
  [string]$Name = 'Robert C. Cain'

  # Static Properties
  static [string] $Version = '2022.01.07.002'

  # Function that returns a string
  [string] TwitterURL()
  {
    $url = "https://twitter.com/$($this.TwitterHandle)"
    return $url
  }

  # Overloaded Function that returns a string
  [string] TwitterURL($twitterHandle)
  {
    $this.TwitterHandle = $twitterHandle
    $url = $this.TwitterURL()
    return $url
  }

  # Function that has no return value
  [void] OpenTwitter()
  {
    Start-Process $this.TwitterURL()
  }

  # Can launch a twitter page without instantiating the class
  static [void] OpenTwitterPage([string] $TwitterHandle)
  {
    # Note here we cannot call the $this.TwitterUrl function
    # because no object exists (hence no $this)
    $url = "http://twitter.com/$($TwitterHandle)"
    Start-Process $url
  }

}

After running this to get the updated class definition in memory let’s call it both ways.

# Create a new instance
$twit = [TwittererRedux]::new()

# Assign the handle, then call TwitterURL
$twit.TwitterHandle = 'ArcaneCode'
$twit.TwitterURL()

Result:

https://twitter.com/ArcaneCode

Now let’s call the overloaded version.

# Now call the overloaded version
$twit.TwitterURL('N4IXT')

Result:

https://twitter.com/N4IXT

We can also check the TwitterHandle property to ensure it has been updated.

$twit.TwitterHandle

Result:

N4IXT

We could have added additional overloads with more parameters. We could have defined [string] TwitterURL($twitterHandle, $anotherParameter), for example.

Other Ways to Differentiate an Overload

In the previous section I stated PowerShell differentiates overloads by the number of parameters passed into our function. There is one other way PowerShell can differentiate, and that is by the data type of each parameter. Let’s look at this simple example.

class over
{
  [string] hello()
    { return 'hello world' }

  [string] hello([string] $name)
    { return "hello string of $name"}

  [string] hello([int] $number)
    { return "hello integer of $number"}
}

As you can see, my class has three overloads. In the second two, we pass in a single parameter. What makes them different though is the data type for the parameter. In the first we use a string, the second an integer. To be accurate then, we need to say PowerShell can tell which overload to call by the number of parameters and the data type of each.

As should be obvious, you will need to strongly type all of your parameters for this to work, but when it comes to classes that’s not a bad idea anyway.

Just as proof, let’s see these working.

$o = [over]::new()
$o.hello()
$o.hello('mom')
$o.hello(33)

Result:

hello world
hello string of mom
hello integer of 33

You can also mix and match, with multiple data types and parameters. For example…

hello([string] $name, [int] $number)
hello([int] $number,  [string] $name)

Both of these are both valid. Just to reiterate, what you could not do is:

hello([string] $nameA, [int] $number)
hello([string] $nameB, [int] $anotherNumber)

PowerShell can’t make the distinction between the two, as they have the same number of parameters and data types in the same order.

Conclusion

Overloads can be very useful when working with classes. They allow you to continue to use method names you are familiar with yet alter them to meet your changing needs.

In our next installment we’ll cover the topic of class constructors, a way to populate properties when you instantiate your new object from the class definition.

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