20

I use Powershell's custom-object command to hold data points. Custom-object creates just one object and assigns a variable to it. Can Powershell go one step further and create new classes from which objects can be made?

In the examples below, I store three pieces of data: a server name, a timestamp, and the minutes since an event occurred on the server.

When I was learning Powershell, I put all this into a two-dimensional array:

$record = @("Server","Timestamp","Minutes")
for ($j = 0; $j -lt 10; $j++){
    $record += @("Server1","$(get-date)",$j)
    sleep 60
    }
$record | export-csv -path c:\record.csv -no type information

export-csv doesn't play well with arrays, so I started using a custom object:

$record = @()
for ($j = 0; $j -lt 10; $j++){
    $r = New-Object -TypeName PSObject
    $r | Add-Member -MemberType NoteProperty -Name Server -Value ""
    $r | Add-Member -MemberType NoteProperty -Name Timesteamp -Value ""
    $r | Add-Member -MemberType NoteProperty -Name Minutes -Value ""
    $r.server = "Server1"
    $r.timestamp = "$(get-date)"
    $r.minutes = "$j"
    $record += $r
    sleep 60
    }
$record | export-csv -path c:\record.csv -no type information

That's exports correctly, and dealing with object properties is easier than dealing with columns in a two-dimensional array.

But if I want to create several custom objects that aren't in an array, I have to write the custom-object code over and over again.

$server1 = New-Object -TypeName PSObject
$server1 | Add-Member -MemberType NoteProperty -Name Server -Value ""
$server1 | Add-Member -MemberType NoteProperty -Name Timesteamp -Value ""
$server2 = New-Object -TypeName PSObject
$server2 | Add-Member -MemberType NoteProperty -Name Server -Value ""
$server2 | Add-Member -MemberType NoteProperty -Name Timesteamp -Value ""
#ad nauseum

What if Powershell could design custom classes in addition to custom objects? Like OO programming languages do? Something like:

class record {
    -MemberType NoteProperty -Name Server -Value ""
    -MemberType NoteProperty -Name Timestamp -Value ""
    -MemberType NoteProperty -Name Minutes -Value ""
    }
$server1 = new-object -TypeName record
$server2 = new-object -TypeName record
$server3 = new-object -TypeName record

Is that possible in Powershell?

Bagheera
  • 1,358
  • 4
  • 22
  • 35
  • 2
    Readers from the future: Note that as of Powershell 5.0 custom classes are a built-in feature using the Class keyword, which supports properties, methods, mutiple constructors, etc, all using typical powershell syntax. No need to learn c# or use the assorted very clever workarounds and kludges described below. – TurtleZero Oct 24 '17 at 14:36
  • Here is the the about_Classes page: https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_classes?view=powershell-5.1 – TurtleZero Oct 24 '17 at 14:39

5 Answers5

38

You can define classes in PowerShell.

Add-Type -Language CSharp @"
public class Record{
    public System.DateTime TimeStamp;
    public string Server;
    public int Minutes;
}
"@;
$MyRecord = new-object Record;
$MyRecord.Server = "myserver";
$MyRecord.Timestamp = Get-Date;
$MyRecord.Minutes = 15;
alroc
  • 27,574
  • 6
  • 51
  • 97
  • 2
    Thanks. I'd never heard of the "Add-Type" command...From a quick read, I enter "Add-Type -Langauge CSharp @" "@...and everything between the double-quotes is compiled as C#? As in, I could paste a complete C# program in between the quotes and it would run as part of my Powershell script? – Bagheera Sep 09 '13 at 21:00
  • You can embed C# in PowerShell. See http://blogs.technet.com/b/stefan_gossner/archive/2010/05/07/using-csharp-c-code-in-powershell-scripts.aspx – alroc Sep 10 '13 at 00:29
  • How do you add functions/methods in CSharp – relipse Nov 24 '15 at 23:05
19

You could use a function as a faux constructor for your custom objects. You wouldn't ever have to duplicate your code, and you could use flags to set your properties right from the function call. Here's an example:

Function New-Constructor
{
    param
    (
        [string]$Name,
        [DateTime]$TimeStamp = (Get-Date)
    )

    $server = New-Object -TypeName PSObject
    $server | Add-Member -MemberType NoteProperty -Name Server -Value $Name
    $server | Add-Member -MemberType NoteProperty -Name TimeStamp -Value $TimeStamp

    # Calling "server" below outputs it, acting as a "return" value
    $server
}

And some sample output:

PS C:\> New-Constructor -Name "MyServer"

Server                                                      TimeStamp
------                                                      ---------
MyServer                                                    9/9/2013 3:27:47 PM


PS C:\> $myServer = New-Constructor -Name "MyServer"
PS C:\> $myServer

Server                                                      TimeStamp
------                                                      ---------
MyServer                                                    9/9/2013 3:27:57 PM


PS C:\> $newServer = New-Constructor -Name "NS" -TimeStamp (Get-Date).AddDays(-1)
PS C:\> $newServer

Server                                                      TimeStamp
------                                                      ---------
NS                                                          9/8/2013 3:33:00 PM

You can do a whole ton of stuff with functions that is out of the scope of this question. Instead, check out about_functions_advanced.

Anthony Neace
  • 25,013
  • 7
  • 114
  • 129
1

Another option.

Properties

You can replace the '$null' value of the property message to have an initial value. The Prop object is a hashtable of keys (properties) and values (initial values).

$messageClass = New-Object -TypeName PSObject -Prop @{ message = $null; }

Methods

$messageClass | Add-Member -MemberType ScriptMethod -Name "ShowMessage" -Value {

    Try
    {
        Write-Host $this.message    
    }
    Catch
    {
        Throw $_.Exception
    }
}

Constructors

The code below describes a constructor. Polymorphism is achieved using [Parameter(Mandatory=$false)] to assert or not the provision of the specified parameter.

function MessageClass {
    param([Parameter(Mandatory=$true)]
          [String]$mandatoryMessage,
          [Parameter(Mandatory=$false)]
          [String]$optionalMessage)

    $messageObj = $messageClass.psobject.copy()

    if ($optionalMessage)
    {
        $messageObj.message = "$mandatoryMessage $optionalMessage!"
    }
    else
    {
        $messageObj.message = "$mandatoryMessage!"
    }

    $messageObj
}

The constructor can then be called like this:

$var1 = 'Hello'
$var2 = 'World'
$example1 = MessageClass -mandatoryMessage $var1
$example2 = MessageClass -mandatoryMessage $var1 -optionalMessage $var2

To show the text:

$example1.ShowMessage()
$example2.ShowMessage()

The results would be:

Hello!

Hello World!

Lionel
  • 21
  • 2
0

For best performance I would do it like that:

Add-Type -TypeDefinition '
public class recordEntry {
    public string server;
    public System.DateTime timestamp;
    public int minutes;

    public recordEntry(string _server, System.DateTime _timestamp, int _minutes) {
        server = _server;
        timestamp = _timestamp;
        minutes = _minutes;
    }
}'

$record = [System.Collections.ArrayList]@()
$record = foreach ($j in 0..10){
    [recordEntry]::new("Server1", [datetime]::Now, $j)
}
$record | export-csv -path c:\record.csv -NoTypeInformation
Carsten
  • 1,612
  • 14
  • 21
0

With powershell 5 and later, you can do that in a more modern way:

class Record {
    [string]$Server
    [string]$Timestamp
    [string]$Minutes

    Record([string]$server, 
           [string]$timestamp, 
           [string]$minutes) {
        $this.Server = $server
        $this.Timestamp = $timestamp
        $this.Minutes = $minutes
    }
}

$server1 = [Record]::new("a", "b", "c")
$server2 = [Record]::new("a", "b", "c")
$server3 = [Record]::new("a", "b", "c")

Note that the constructor is optional, so you could also wire your servers with:

$server1 = [Record]::new()
$server1.Server = "a"
$server1.Timestamp = "b"
$server1.Minutes = "c"

Reference: https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_classes?view=powershell-7.3#syntax

Jiehong
  • 786
  • 1
  • 7
  • 16