Classes 

With version 5, classes have been introduced to PowerShell. If you are coming from a developer background, you may already be familiar with classes. A class is a data structure for storing properties and methods. Classes in PowerShell, though, have some limitations and are very rarely used. The most common use case is their implementation for the Desired State Configuration, which we will dive into in a later chapter. Therefore, we will give you only a short introduction to classes, and provide further links and material that you can have a look at.

The creation of a simple class starts with the class keyword. The class description is like a model. Think of it as a recipe for a fantastic PowerShell cake. It can be used to create hundreds of cakes.

You should always add a constructor to the class. A constructor is the first code that will be executed, and is always named the same as the class itself.

The first example is very straightforward:

#Class with constructor
class FantasticPowerShellCake
{
#Constructor without values
FantasticPowerShellCake()
{}

#method returning a string
[string] returnSomething()
{
return "something"
}
}

The class can be loaded by highlighting the complete class and executing it by pressing F8. You can recognize the methods by the use of braces, which are followed by braces containing the code that is going to be executed. Now, let's try to create a real cake out of this recipe:

#Instantiate
$fantasticPowerShellCake = [FantasticPowerShellCake]::new()

#Instance
$fantasticPowerShellCake

#Method
$fantasticPowerShellCake. #IntelliSense - try to press CTRL + Space
$fantasticPowerShellCake.returnSomething()

In addition to instantiable classes, static classes are also available. Static classes cannot be instantiated, and therefore don't have a constructor method. In addition, a class can have static methods and properties. These specific methods and properties don't need the class to be instantiated and can be used directly on the type. A static class, therefore, can have properties and methods to retrieve and set properties, or do other specific actions, which in most cases need parameters. There are some very helpful examples available. One such example is the Environment class:

#Static Class of System Environment
[System.Environment]::OSVersion.Version

In this dedicated example, we query  System.Environment and its OSVersion.Version property to retrieve an object of System.Version, which looks like this:

And piping the object to Get-Member shows the aforementioned type:

Another example is the Math static class, which contains a lot of useful methods for daily use:

#region Properties

#PI
[Math]::PI #3,14159265358979

#E
[Math]::E #2,71828182845905

#endregion

#region Methods

#Showing all static methods
[System.Math] | Get-Member -Static -MemberType Methods

[Math]::Min
<#
OverloadDefinitions
-------------------
static sbyte Min(sbyte val1, sbyte val2)
static byte Min(byte val1, byte val2)
static int16 Min(int16 val1, int16 val2)
static uint16 Min(uint16 val1, uint16 val2)
static int Min(int val1, int val2)
static uint32 Min(uint32 val1, uint32 val2)
static long Min(long val1, long val2)
static uint64 Min(uint64 val1, uint64 val2)
static float Min(float val1, float val2)
static double Min(double val1, double val2)
static decimal Min(decimal val1, decimal val2)
#>
[Math]::Min(3,9) #3

[Math]::Pow
<#
OverloadDefinitions

-------------------
static double Pow(double x, double y)
#>

[Math]::Pow(2,3) # 2^3 = 2*2*2 = 8

#endregion

As you can see, executing the method without the braces returns all overloading of the methods. These methods can therefore be called with different types of parameters, and sometimes also with a different number of parameters, doing similar or different actions in their execution. By using Get-Member with the -Static flag, all static methods and properties can be retrieved.

In the following example, SecretStorer, we will add more complexity to the class:

#Class with constructor
class SecretStorer
{
#Constructor without values
SecretStorer()
{
}
#Constructor with values
SecretStorer([string]$secretString, [int]$secretInt)
{
$this.secretInt = $secretInt
$this.secretString = $secretString
}

#Property - string
[string]$secretString

#Property - int
[int]$secretInt

#Method
[string] returnSomething()
{
return "SecretString: $($this.secretString) $([System.Environment]::NewLine)SecretInt: ($this.secretInt)"
}

#Method for string
storeSecret([string]$secret)
{
$this.secretString = $secret
}

#Method - overloaded for integer
storeSecret([int]$secret)
{
$this.secretInt = $secret
}
}

There is now an added and overloaded constructor for the class. Depending on the number and type of the parameters, the correct method is being called. Also, two properties have been included—$secretString and $secretInt—plus a method called storeSecret() with two overloads (the .NET version of the parameter sets we saw in Chapter 4, Advanced Coding Techniques) to modify these properties after an instance of the class (a new object) has been created. Try to load the class and go through the next examples. Don't forget that the code can be viewed and downloaded from GitHub:

#Instantiate
$instance = [SecretStorer]::new()

#Instance
$instance

#Instantiate with values
$instance = [SecretStorer]::new('PowerShell is great!',1337)

#Instance
$instance

#Properties
$instance.secretInt
$instance.secretString

#Properties - validating types
($instance.secretInt).GetType()
($instance.secretString).GetType()

#Store
$instance.storeSecret('PowerShell is awesome!')

#take a look at the properties
$instance.returnSomething()

#Store
$instance.storeSecret(1338)

#take a look at the properties
$instance.returnSomething()

#ToString() automatically returns the name of the type
$instance.toString()

Now you know basically how a simple class can be created with some methods and properties, with a constructor, and also possible overloading of the methods. Next, we will increase the complexity a little bit further. From professional software development there exist different approaches to create reusable and dynamic code. One very famous mechanism is the use of polymorphism. PowerShell is an object-oriented programming language. This means that everything you use within PowerShell is derived from a specific object. Let's take a look at the string type with the following code:

#Polymorphism
"string".GetType()
"string".GetType() | Select-Object * | Out-GridView

On executing the first line of code, you will retrieve the name of the type, which is String,  as well as the base type, which is  System.Object. This means that every string is derived from the System.Object type, and can also use its properties and methods.  In the second line of code, you can see even more information such as DeclaredConstructorsDeclaredEventsDeclaredFieldsDeclaredMembersDeclaredMethodsDeclaredNestedTypes, and DeclaredProperties.

For a detailed description of polymorphism, take a look at the following link: https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/polymorphism

PowerShell is based on .NET, and there are many similarities between C# and PowerShell. If you cannot find a solution to your problem that is written in PowerShell, it is always worth to search for the same issue in C#, as the two languages don't differ too much.

We will just continue to work with our previous example and extend it. Therefore, a new class, SecretStorerv2, is created, which is derived from our previously used class, SecretStorer:

#Class with constructor
class SecretStorerv2 : SecretStorer
{
#Constructor without values
SecretStorerv2(): base()
{ }

#Constructor with values
SecretStorerv2([string]$secretString, [int]$secretInt): base([string]$secretString, [int]$secretInt)
{ }

#Method - override toString() method

[string] ToString()
{
return $this.returnSomething()
}
}

The constructors are also derived from the base constructor, which in our example comes from the SecretStorer class. In addition, we now override the ToString() method. Every object of the System.Object type integrates a method for ToString(). This also includes our newly created SecretStorer class.

As shown previously, this method returns the name of the object and would, therefore, return the SecretStorer string. We want to change this behavior with our new SecretStorerv2 class, in order to return the class properties and their values instead. For this, we have already created the returnSomething() method in our base class. Now, we just need to add a method with the name ToString(), returning the type [string] to override it. So, every time an instance of the object would previously call its ToString() method, it would now call our overridden method.

With the $this variable, you just refer to the currently created instance and call its returnSomething() method. You can seee the result with the following code:

#Instantiate with values
$instance = [SecretStorerv2]::new('ToStringTest',1234)

#Call method
$instance.returnSomething()

#ToString() is now overloaded with our specified method - 3 examples
"$instance"
$instance.toString()
$instance

In the previous example, the returnSomething() method is being called three times, and you will retrieve the information of the properties. The first call, $instance, implicitly calls the ToString() method, thus calling returnSomething(). The second call, $instance.toString() is a bit more blunt and by calling ToString() directly, also executes returnSomething(). The last call simply puts the object itself on the output stream.

For performance reasons, and because of the coding overhead, classes are rarely used in PowerShell. In addition to other programming languages, some more complex techniques for classes are still missing.

A more complex scenario with the use case for dynamically parsing log files can be found here:

Codehttps://github.com/ddneves/LogFileParser
Articlehttps://blogs.msdn.microsoft.com/daviddasneves/2017/10/27/logfileparser-with-powershell/

Classes have the benefit of being dynamically extendable, which was shown in this example.
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.133.144.197