Introduction to PowerShell 5.0 Classes

Introduction to PowerShell 5.0 Classes

Since the first release of Windows 10 back in July 2015, PowerShell 5.0 is available for production usage. The backwards compatible Windows Management Framework 5.0, which includes PowerShell 5.0 for older Windows Operating Systems, was released on February 2016.

The new PowerShell 5.0 brings along many great new features, all described in a dedicated TechNet article: What’s New in Windows PowerShell. In this post, I will cover the new possibility of creating custom objects based on classes. To understand how important the new possibility is, we have to look back how we’ve use custom objects in the past.

Custom objects in the past

Since PowerShell 1.0, custom objects can be created with the New-Object cmdlet combined with the PSObject class. Because creating custom objects without any properties makes no sense, the Add-Member cmdlet was the only way to add these custom properties:

$MyCar = New-Object -TypeName PSObject
$MyCar | Add-Member -MemberType NoteProperty -Name 'Number' -Value 'CAR0001'
$MyCar | Add-Member -MemberType NoteProperty -Name 'Color' -Value 'Red'

With the following version 2.0, the New-Object cmdlet got a new parameter called -Property. With this new parameter, the properties can be initialize during the custom object construction. The properties have to be specified inside a hashtable, where the key is the property name and the value will be the property value.

$MyCar = New-Object -TypeName PSObject -Property @{
    Number = 'CAR-0001'
    Color  = 'Red'
}

One problem with this approach was,the desired order of the specified properties was not be kept after creation. The root cause is, that the used hashtable (type of System.Collections.Hashtable) is not ordered. To solve this problem, a new accelerator PSCustomObject was introduced in PowerShell 3.0. It does the same job as shown in the example before, but it will keep the order of the properties. And in addition, it’s shorter to write which makes the code easier to read.

$MyCar = [PSCustomObject] @{
    Number = 'CAR-0001'
    Color  = 'Red'
}

All three previous examples will create custom objects which are of type System.Management.Automation.PSCustomObject. In general, this is no problem until it’s important to distinguish the created objects by their types. As an example, the object type is relevant when its used together with PowerShell format files.

To add a custom type, a small trick or hack is available, as you can see in the next code block. By using the Insert() method on the PSTypeNames object property, a custom type can be injected. If you enumerate the custom object with Get-Member, the new type was shown. But under the hood, the object was still of type PSCustomObject. You can check that with the GetType() .NET method.

$MyCar = [PSCustomObject] @{
    Number = 'CAR-0001'
    Color  = 'Red'
}

$MyCar.PSTypeNames.Insert(0, 'Car')

$MyCar | Get-Member
$MyCar.GetType()

Classes

With PowerShell 5.0, finally, we have a new language keyword called class to defined real classes nut just custom objects. If you are familiar with the object-oriented programming concept, this will be just a new syntax, PowerShell hasn’t reinvent the wheel.

First, let us create a custom class to replace the Car example we’ve used before. The first line defines the class and it’s name. On the following lines, the class members are specified. As best practice, the type for all members should be defined. Because we are still using PowerShell, some concept off the advanced functions can be reused with classes. In this case, the color is validated with the annotation [ValidateSet()]. It will throw an error, if we try to assign any different value as listed in the validation set.

class Car
{
    [String] $Number

    [ValidateSet('White', 'Black', 'Red')]
    [String] $Color
}

$MyCar = [Car]::new()

$MyCar.Number = 'CAR-0001'
$MyCar.Color  = 'Red'

After the class definition itself, which is surrounded by { and }, a new object of the Car class can be instantiate. To do this, PowerShell has introduced a new static method called new(), which is available on the class type itself.

Now we have our first object based on a PowerShell class. You can verify the type with the Get-Member cmdlet and the GetType() .NET method, as we’ve used before. You will see, our object is of type Car. All members we specify will be public. It’s currently not possible to add private or protected members. Next, we extend our class with a constructor.

class Car
{
    [String] $Number

    [ValidateSet('White', 'Black', 'Red')]
    [String] $Color

    Car([String] $Number, [String] $Color)
    {
        $this.Number = $Number
        $this.Color  = $Color
    }
}

$MyCar = [Car]::new('CAR-0001', 'Car')

Thanks to the constructor, which has the same name as the class itself, we are now able to initialize the class members at creation time. The values can be passed to the new() method. The last missing core concept off classes are the methods. Methods can be specified the same way as the constructors. In addition, the methods have a defined return type. This can be any type. In our case, we will not return anything, therefore we use the void type.

class Car
{
    [String] $Number

    [ValidateSet('White', 'Black', 'Red')]
    [String] $Color

    [Int32] $Milage = 0

    Car([String] $Number, [String] $Color)
    {
        $this.Number = $Number
        $this.Color  = $Color
    }

    [void] Drive([UInt32] $Distance)
    {
        $this.Milage += $Distance
    }
}

$MyCar = [Car]::new('CAR-0001', 'Red')

$MyCar.Drive(10)

$MyCar.Milage

Enumerations

Instead of using the color property with a validation set, since PowerShell 5.0, we are also able to use enumerations. Therefore the language keyword enum has been introduced. The following example shows, how an enumeration is defined inside PowerShell, which then can be used inside the script.

enum CarColor
{
    White
    Black
    Red
}

class Car
{
    [String] $Number

    [CarColor] $Color

    [Int32] $Milage = 0

    Car([String] $Number, [CarColor] $Color)
    {
        $this.Number = $Number
        $this.Color  = $Color
    }

    [void] Drive([UInt32] $Distance)
    {
        $this.Milage += $Distance
    }
}

$MyCar = [Car]::new('CAR-0001', [CarColor]::Red)

Class Inheritance

In object-oriented programming languages, the inheritance of classes is one of the most powerful features. This is available inside PowerShell too. This following example gives you an idea, how the generic Car class can be extended.

class Car
{
    [String] $Number

    [String] $Type

    Car([String] $Number, [String] $Type)
    {
        $this.Number = $Number
        $this.Type   = $Type
    }
}

class SportCar : Car
{
    [Int32] $Horsepower

    SportCar([String] $Number, [Int32] $Horsepower) : base($Number, 'SportCar')
    {
        $this.Horsepower = $Horsepower
    }
}

class Minivan : Car
{
    [Int32] $Seats

    Minivan([String] $Number, [Int32] $Seats) : base($Number, 'Minivan')
    {
        $this.Seats = $Seats
    }
}

$MySportCar = [SportCar]::new('CAR-0001', 250)
$MyMinivan  = [Minivan]::new('CAR-0002', 10)

Conclusion

This new feature does not mean, that we should always use classes for each custom objects. The previous methods with PSCustomObject are still valid. Especially for objects which are more or less only used to store or transfer data, the PSCustomObject is a good choise. If you create multiple complex functions updating or accessing the properties of one single object, then the new class feature of PowerShell is very handy.

Due to the current low spread of Windows 10 and WMF 5.0 installations, this feature can rarely be used yet. But if your script runs inside a PowerShell 5.0 environment, you are free to use it and replace the old PSCustomObjects, where it makes sense.

As usual in PowerShell, there is also a dedicated help file available for classes which is available inside the TechNet PowerShell portal: about_Classes.

Leave a Reply

Your email address will not be published. Required fields are marked *