Three part series

C# to PowerShell: Finale

Welcome to the third and final C# to PowerShell article.

.NET in PowerShell

Yes, you read that correctly. You can access .NET Framework Classes from within PowerShell. PowerShell utilizes the .NET Framework behind the scenes, so it only makes sense you can access it.

.NET Framework Versions

The table below shows the installed PowerShell and it’s required .NET Framework version which is also preinstalled.

Windows Version PowerShell/.NET Installed Version
Windows Server 2019 PowerShell 5.1 / .NET Framework 4.5.2
Windows Server 2016 PowerShell 5.1 / .NET Framework 4.5.2
Windows 10 1607+ PowerShell 5.1 / .NET Framework 4.5.2
Windows 10 version 1507, 1511 PowerShell 5.0 / .NET Framework 4.5.2
Windows Server 2012 R2 PowerShell 4.0 / .NET Framework 4.5.2
Windows 8.1 PowerShell 4.0 / .NET Framework 4.5.2
Windows Server 2012 PowerShell 3.0 / .NET Framework 4
Windows 8 PowerShell 3.0 / .NET Framework 4

In a perfect world your machines would be in the top three rows of this table, but if you happen to have some 2012 R2 servers hanging around you can install PowerShell 5.1 on them without issue, just requires a reboot after the install.

Using .NET Classes in PowerShell

Microsoft did a fantastic job of ensuring PowerShell could do just about everything, but sometimes you need a little more. I recently came across a situation where I needed to join a path in a script. Since PowerShell scripts can be cross platform now I couldn’t hardcode the path using ” as it’s not valid on *nix systems. I used Join-Path which has a drawback.

Join-Path C: TimDavis
'C:TimDavis'
Join-Path C: TimDavis Test
Join-Path : A positional parameter cannot be found that accepts argument 'Test'.
....

Join-Path in PowerShell 5.1 does not accept multiple child paths (the TimDavis Test parts) so I would’ve needed to loop this and that is craziness.

Enter our trusty friend System.IO.Path and a useful Method called Combine. Combine will handle however many arguments or sub paths you give it and combine it to one path using the OS folder separator. The good news this same method exists in .NET Core.

[IO.Path]::Combine("C:", "TimDavis", "Test")
'C:TimDavisTest'

If you are using VSCode or the built in PowerShell ISE you will get autocompletion just like in Visual Studio

This is a pretty powerful feature, it means if a Microsoft provided command doesn’t do something in a way you need then you can revert back to .NET classes. I have yet to find something that doesn’t work, but be warned I’m sure there is. A side note, I don’t find myself using .NET classes nearly as often as I thought I would.

To read more about it check out the Creating .NET and COM Objects (New-Object) and Using static classes and methods over on Microsoft Docs.

PowerShell Classes

This is the feature I’m most excited about. PowerShell has Classes! Not only does it have Classes it has Classes that follow the rules of….well Classes.

These Classes have everything you would expect, Properties, Methods and Constructors as well as Access Modifiers (hidden and static).

Create a Class

Lets start with a simple Animal class. All classes start with ‘class’ then a Name and contents of the class are surrounded by your friendly ‘{‘ which should be very familiar to you. Extremely similar to C#, the big difference is it’s already public so no need to add an access modifier.

class Animal {
 [string]$Color
 [string]$Size
 [bool]$Stripes

 [string]ToString(){ 
 return "I'm a $($this.Size) $($this.Color) animal"
 }
}

An Animal has three properties that define it, Color, Size and Stripes. As you can probably guess by looking at the code, the [string] defines the type, this is also how you Type Cast in PowerShell. An Animal also has a classic ToString() method which returns a string.

Instantiating a Class

You have a class, now what, how do you use it?

$animal = [Animal]::new()
$animal

Color Size Stripes
----- ---- -------
 False

It really is simple. [TYPE]::new() is the PowerShell equivalent of new TYPE(). Since this is PowerShell we can also utilize the standard capabilities of PowerShell to output the information about the instance we created without even using the ToString() method.

Accessing Properties and Methods

Accessing Properties and Methods should feel natural as the process doesn’t change at all.

$animal.ToString()
"I'm a animal"

$animal.Color = "Brown"
$animal.Size = "Large"
$animal.ToString()
"I'm a Large Brown animal"

$animal

Color Size Stripes
----- ---- -------
Brown Large False

First we call the ToString() method and see it just doesn’t look right. Next we assign values to the Color and Size properties then call ToString() method again. Lastly we let PowerShell output the class to screen.

What About Inheritance?

Yep! PowerShell has it of course, even in your custom Classes. To demonstrate this I’m going to create a new class called Tiger which inherits from Animal.

class Tiger : Animal {
 [string]$Breed

 Tiger([string]$breed){
 $this.Stripes = $true
 $this.Color = "Orange"
 $this.Size = "Large"
 $this.Breed = $breed
 }

 [string]ToString() {
 return "I'm a $($this.Size) $($this.Color) Tiger"
 }
}

This will look a little different. I’ve added a Constructor to Tiger. Inheritance syntax is the same as C#, Microsoft couldn’t have made it any easier than that.

The constructor should look very familiar as well, similar to C# but without the public modifier. Since this is derived from Animal I’m able to set the base class properties.

Let see this in action

$tiger = [Tiger]::new("Bengal")

$tiger.ToString()
"I'm a Large Orange Tiger"

$tiger

Breed Color Size Stripes
----- ----- ---- -------
Bengal Orange Large True

Looks and acts the way we expect it to. We create a new Tiger object while passing the Breed to the constructor. We print using ToString() then using PowerShell standard output.

Polymorphism?

Yep! Works like a C# developer would expect it to. To demonstrate this I’ve created a PowerShell Function that expects an Animal as an argument, but we are going to pass it a Tiger instead. Should work because of Polymorphism right?

Function AnimalTest([Animal]$mine) {
 $mine.ToString()
 $mine.GetType().Name
}

AnimalTest($animal)
"I'm a Large Brown animal"
"Animal"

AnimalTest($tiger)
"I'm a Large Orange Tiger"
"Tiger"

It does!

I’ve just covered some of the basics on PowerShell Classes and is by no means a comprehensive guide. The Microsoft Documentation on PowerShell Classes is a great starting point.

In this installment we have talked about how you are not limited to only Microsoft provided PowerShell commands. I showed you that you can use your treasured C# classes and methods if needed. Most of the time you won’t need it but you have options and they are supported options, not hacks or unofficial methods.

We have also dipped our toes in to PowerShell Classes. We learned that PowerShell Classes behave and function almost identically to our C# counter parts with just a little differences in syntax.

I hope this three part series has taken some of the hesitation out of your decision to give PowerShell a try. It is a struggle not to end your lines with ‘;’ and to always start a variable with ‘$’ but you will get better with practice. There are numerous resources available to help you on this path, I recommend bookmarking Microsoft PowerShell Documentation for starters.