Why Do We Need Constructors?

While traveling after PowerShell Summit Europe in Stockholm, I was honored to be the guest of the Microsoft Technical User Group in Oslo, Norway (@MTUG_Norge). This well-established user group meets on the beautiful campus of the University of Oslo, a short light-rail ride from downtown Oslo.

We got together for a hands-on lab on classes in PowerShell 5.0. It was an experienced group — mainly IT operations folks — who are well-versed in PowerShell. The concepts are all new, but the group of 35-40 people were quick to understand them.

One thoughtful guy asked a particularly good question: Why do we need constructors? As PowerShell scripters, we’re used to the New-Object cmdlet and creating objects with PSCustomObject, PSObject, and hash tables. Why do we need yet another concept in object creation?

Why Constructors?

The short answer is that, unbeknownst to us, we’ve been using constructors all along. But, in its quest to make us successful, PowerShell has (mostly) hidden the concept from us.

A constructor is a special method of a class that initializes new objects or instances of the class. Without a constructor, you can’t create instances of the class. Imagine that you could create a class that represents files, but without constructors, you couldn’t create any files based on the class.

Some types of classes, including abstract classes and interfaces, don’t need constructors, because you don’t create instances of them. Instead, you use them as base classes and derive subclasses from them.

(Cool digression: Abstract classes aren’t required to have constructors, but they can have them. You can call the constructor of an abstract class to create a new instance of a subclass that’s based on (“derived from”) the abstract class. Thanks to Doug Finke for his excellent tutelage.)

(Fun fact: You can implement multiple interfaces, but you can derive a class from only one base class.)

How do you use a constructor?

Even though constructors are methods, you don’t call them directly.

Instead, constructors determine the parameter values or “arguments” that you are required to provide to the New-Object cmdlet or the New static method.

For example, let’s create a Tree class that has two properties, Species (a string) and Height (an integer).

class Tree {
    [String]$Species
    [int32]$Height}

The Tree class also has one constructor that takes one string value (for the species) and one integer value (for the height).

# In Help: Constructor1: [String]$Species, [Int32]$Height

When you create a tree, you must provide precisely one string and one integer in the specified order. (The order matters because you don’t type parameter names.)

New-Object -TypeName Tree -ArgumentList 'Mimosa', 10

-or-

[Tree]::New('Mimosa', 10)

This works:

Species Height
------- ------
Mimosa 10

If you try to create a Tree with missing, extra, or different objects, the command fails.

PS C:\> New-Object -TypeName Tree -ArgumentList 10

New-Object : Cannot find an overload for "Tree" and the argument count: "1".
At line:1 char:1
+ New-Object -TypeName Tree -ArgumentList 10
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [New-Object], MethodException
+ FullyQualifiedErrorId : ConstructorInvokedThrowException,Microsoft.PowerShell.Commands.NewObjectCommand

How do I write a constructor?

Constructors initialize the new object, that is, they set the startup property values for the object. They might also do other things necessary to make the object usable.

You can distinguish constructors from other methods of a class because constructors always have the same name as the class. If the class is Tree, it’s constructors are all named Tree, too.

A constructor is a method, so it has a script block and (optional) parameters.

Syntax:

<ClassName> ( [<optional_parameters>] ) {
    <statements and commands…>
}

For example, the Tree class has a constructor that takes a Species string. Like the class, the constructor is named Tree.

Tree ( [string]$Species ) {
    $this.Species = $Species
}

You can have multiple constructors. But, because the parser looks only at parameter types, not names, the constructors must take different types.

This constructor in the Tree class takes a string and an integer. It assigns the string to the Species property and the integer to the Height property.

Tree ( [string]$Species, [int32]$Height ) {
 
    $this.Species = $Species
    $this.Height = $Height
}

Here is the tree class with its Tree constructors.

class Tree ()
{
 
    [String]$Species
    [int32]$Height
 
    # Here is a constructor. Notice the name.
    Tree ( [string]$Species ) {
        $this.Species = $Species
    }
 
    # Here is another constructor. It's also named Tree, but it takes different parameter types.
    Tree ( [string]$Species, [int32]$Height ) {
        $this.Species = $Species
        $this.Height = $Height
    } 
 
    # Here is a different method with a different name.
    [int32] Grow ( [uint32]$Amount) )
    {
        $this.Height += $Amount
        return $this.Height
    }
}

Constructors don’t return anything. A return type is not permitted.

To use the first constructor, I need to submit a string, such as ‘Mimosa’.

New-Object -Typename Tree -ArgumentList 'Mimosa'

-or-

[Tree]::New('Mimosa')

To use the second constructor, I submit a string and an integer, in that order.

New-Object -Typename Tree -ArgumentList 'Mimosa, 10'

-or-

[Tree]::New('Mimosa', 10)

How do I find the constructors of a class?

The simplest way to get the constructors in a class is to use the New static property of all classes.

The syntax is:

[<ClassName>]::New

For example, to get the constructors in the Tree class:

PS C:\> [Tree]::New

OverloadDefinitions
-------------------
Tree new(string Species)
Tree new(string Species, int Height)

Must I write a constructor?

Surprisingly, the answer is no.

When you create a class, Windows PowerShell creates a default constructor for you. A default constructor has no parameters and takes no arguments or values. You might hear it called a null constructor or parameter-less constructor.

For example, if I create a Tree class with nothing in it, I can create a Tree object.

PS C:\> class Tree {}
PS C:\> $myTree = New-Object -TypeName Tree
PS C:\> $myTree | Get-Member

   TypeName: Tree

Name        MemberType Definition
----        ---------- ----------
Equals      Method     bool Equals(System.Object obj)
GetHashCode Method     int GetHashCode()
GetType     Method     type GetType()
ToString    Method     string ToString()

If you use the New static property of the Tree class, you can see the default constructor that Windows PowerShell created for you.

PS C:\> [Tree]::New

OverloadDefinitions
-------------------
Tree new()

However, if you add a constructor to the Tree class, the “free” default constructor disappears.

PS C:> class Tree {
>> Tree ([String]$Species) {}
>> }
>>
PS C:\> [Tree]::New

OverloadDefinitions
-------------------
Tree new(string Species)

If you want a default constructor in your class, you need to add it explicitly. (And, you should, because it allows people to use hash tables to create objects. I’ll explain in detail in a later blog post.)

PS C:\> class Tree {
>>
>> Tree () {}
>> Tree ([String]$Species) {}
>> }
>>
PS C:\> [Tree]::New

OverloadDefinitions
-------------------
Tree new()
Tree new(string Species)

 

Constructors are very new to Windows PowerShell users, but we’ve been using them whenever we use the New-Object cmdlet, and Windows PowerShell has been using them to create objects whenever we use a Get cmdlet. Now, in Windows PowerShell 5.0, we can create them, as well as use them.

Thanks again to MTUG_Norge and its members for having me. I really enjoyed it.

June Blender is a technology evangelist at SAPIEN Technologies, Inc and a Windows PowerShell MVP. You can reach her at juneb@sapien.com and follow her on Twitter at @juneb_get_help.