Can I write a class using PowerShell?
With PowerShell being built on top of the .NET framework, can I write my own custom class using PowerShell?
I'm not talking about instantiating .NET classes... that part is plain enough. I want to write my own custom classes using PowerShell scripts. Is it possible? So far my research leads me to say that this isn't possible.
I'm still learning PowerShell, and so far I haven't found an answer on this website, despite a few searches.
Take a look at the Add-Type cmdlet. It lets you write C# and other code in PowerShell. For example (from the above link), in a PowerShell window,
$source = @"
public class BasicTest
{
public static int Add(int a, int b)
{
return (a + b);
}
public int Multiply(int a, int b)
{
return (a * b);
}
}
"@
Add-Type -TypeDefinition $source
[BasicTest]::Add(4, 3)
$basicTestObject = New-Object BasicTest
$basicTestObject.Multiply(5, 2)
I suspect that the solution that you are looking for is PowerShell Modules. They perform the roles that classes typically perform in other languages. They give you a very simple, yet structured, way to reuse your code.
Here is how to get the functionality of classes in PowerShell using modules. At the command line you could do this:
New-Module -ScriptBlock {function add($a,$b){return $a + $b}; function multiply($a,$b){return $a * $b}; function supersecret($a,$b){return multiply $a $b}; export-modulemember -function add, supersecret}
Then you would be able to:
PS C:\> add 2 4
6
PS C:\> multiply 2 4
The term 'multiply' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
At line:1 char:9
+ multiply <<<< 2 4
+ CategoryInfo : ObjectNotFound: (multiply:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException
PS C:\> supersecret 2 4
8
As you can see multiply is private within the module. More traditionally you would instantiate an object that is an instance of the module. That is done via the -AsCustomObject parameter:
$m = New-Module -ScriptBlock {function add($a,$b){return $a + $b}; function multiply($a,$b){return $a * $b}; function supersecret($a,$b){return multiply $a $b}; export-modulemember -function add, supersecret} -AsCustomObject
Then you could:
PS C:\> $m.add(2,4)
6
PS C:\> $m.multiply(2,4)
Method invocation failed because [System.Management.Automation.PSCustomObject] doesn't contain a method named 'multiply'.
At line:1 char:12
+ $m.multiply <<<< (2,4)
+ CategoryInfo : InvalidOperation: (multiply:String) [], RuntimeException
+ FullyQualifiedErrorId : MethodNotFound
PS C:\> $m.supersecret(2,4)
8
This all demonstrates the use of dynamic modules meaning nothing is stored to disk for reuse. It is fine for very simple functionality. If you want to actually be able to read the code and reuse it in future sessions or scripts, however, you would want to store it in a .psm1 file and then store that file in a folder with the same name (minus the extension) as the file. Then you can import the module into your session at the command line or into another script.
As an example of this, let's say I took this code:
function Add{
param(
$a,
$b
)
return $a + $b
}
function Multiply{
param(
$a,
$b
)
return $a + $b
}
function SuperSecret{
param(
$a,
$b
)
return Multiply $a $b
}
Export-ModuleMember -Function Add, SuperSecret
And saved it to a file called TestModule.psm1 in the folder: C:\Windows\System32\WindowsPowerShell\v1.0\Modules\TestModule
The Modules folder in the PowerShell install folder is a magic folder and any modules stored there are visible to the Import-Module cmdlet without having to specify a path. Now if we run Get-Module -List
at the command line we see:
ModuleType Name ExportedCommands
---------- ---- ----------------
Script DotNet {}
Manifest FileSystem {Get-FreeDiskSpace, New-Zip, Resolve-ShortcutFile, Mount-SpecialFolder...}
Manifest IsePack {Push-CurrentFileLocation, Select-CurrentTextAsVariable, ConvertTo-Short...
Manifest PowerShellPack {New-ByteAnimationUsingKeyFrames, New-TiffBitmapEncoder, New-Viewbox, Ne...
Manifest PSCodeGen {New-Enum, New-ScriptCmdlet, New-PInvoke}
Manifest PSImageTools {Add-CropFilter, Add-RotateFlipFilter, Add-OverlayFilter, Set-ImageFilte...
Manifest PSRss {Read-Article, New-Feed, Remove-Article, Remove-Feed...}
Manifest PSSystemTools {Test-32Bit, Get-USB, Get-OSVersion, Get-MultiTouchMaximum...}
Manifest PSUserTools {Start-ProcessAsAdministrator, Get-CurrentUser, Test-IsAdministrator, Ge...
Manifest TaskScheduler {Remove-Task, Get-ScheduledTask, Stop-Task, Add-TaskTrigger...}
Manifest WPK {Get-DependencyProperty, New-ModelVisual3D, New-DiscreteVector3DKeyFrame...
Manifest AppLocker {}
Manifest BitsTransfer {}
Manifest PSDiagnostics {}
Script **TestModule** {}
Manifest TroubleshootingPack {}
Manifest Citrix.XenApp.Commands... {}
We can see that our module is ready to import. We can import it into the session and use it in the raw using:
Import-Module TestModule
Or once again we can instantiate an object:
$m = Import-Module TestModule -AsCustomObject
You can use the class
keyword that was introduced in PowerShell 5.0
Here is an example by Trevor Sullivan. (Archived here.)
##################################################
####### WMF 5.0 November 2014 Preview ###########
##################################################
class Beer {
# Property: Holds the current size of the beer.
[Uint32] $Size;
# Property: Holds the name of the beer's owner.
[String] $Name;
# Constructor: Creates a new Beer object, with the specified
# size and name / owner.
Beer([UInt32] $NewSize, [String] $NewName) {
# Set the Beer size
$this.Size = $NewSize;
# Set the Beer name
$this.Name = $NewName;
}
# Method: Drink the specified amount of beer.
# Parameter: $Amount = The amount of beer to drink, as an
# unsigned 32-bit integer.
[void] Drink([UInt32] $Amount) {
try {
$this.Size = $this.Size - $Amount;
}
catch {
Write-Warning -Message 'You tried to drink more beer than was available!';
}
}
# Method: BreakGlass resets the beer size to 0.
[void] BreakGlass() {
Write-Warning -Message 'The beer glass has been broken. Resetting size to 0.';
$this.Size = 0;
}
}
This works on Windows 10 Pro.
Test drive it like this:
# Create a new 33 centilitre beer, named 'Chimay'
$chimay = [Beer]::new(33, 'Chimay');
$chimay.Drink(10)
$chimay.Drink(10)
# Need more beer!
$chimay.Drink(200)
$chimay.BreakGlass()