Monday, November 10, 2014

Writing Classes With PowerShell V5-Part 1

As I noted in previous blog posts (here and here) one of the many cool features coming with PowerShell V5 is the ability to develop Classes and Enums using PowerShell. in Monday's blog post, I showed how you could create an Enum, one important construct useful to creating classes.

Before I look at creating classes with PowerShell V5, it might make sense to go over what a class is and what creating them means. Many PowerShell users may be under familiar with the concepts involved.

Classes

The first thing to cover is classes. But what IS a class? A class is the definition of an object. In PowerShell everything you do relates to instances of objects that belong to some class. If you issue Get-ChildItem on the file system provider, you get zero, one, or more object occurrences, or instances, of either the System.Io.DirectoryInfo or System.Io.FileInfo classes. The number 42 is an object of the class System.Int32,

The Class defines the object (and occurrences of that object). Each class has a number of members. These include constructors, properties, and methods. The constructor is how you create a new class instance (creating a System.Io.Fileinfo object (instance) pointing to c:\foo.bar.ps1). Properties are attributes of object are actions you can apply to the object, such as the name of a file. The  properties and methods of a class can be either static (belonging to the class) or dynamic (belonging to a class instance). This is a difference new PowerShell – see below for more on the difference.

Constructors

A constructor is code that creates a new instance of the class. Constructors can take parameters that describe the object instance to be created. You can also overload constructors – creating more than one constructor with different sets of parameters. In the System.Net.Mail.MailMessage class, you can see 4 different constructors; one takes no parameters, while the other three take different sets of parameters, as you can see in MSDN. Each constructor creates an object instance (a mail message) but with different properties.

Properties

A properties is some attribute, either of the class itself or of an class instance. For example, the class [system.int32]. Each property has a type and this can be a value type (such as a string, or an integer) or a more complex object. An occurrence of the System.Net.Mail.MailMessage  class, for example, has a simple Priority property that takes a string (Low, High, Normal). It also has a property, To, which takes an object of the class System.Net.Mail.MailAddresss. The point here is that an object instance (a mail message) has a property (to) that itself is an object which has properties (e.g. DisplayName). Of course, that property too could be an object etc etc (although in the case of System.Net.MailAddress it doesn't!).

Methods

A method is some function that the class is capable of performing – either on an specific instance (a dynamic or instance method) or based on the class (a static method). Dynamic methods usually do something to the instance, while static methods do something that relates to class itself. The System.Int32 class, for example, has both static and instance based methods. Individual instances of the the System.Int32 class contain methods that convert the instance's value to another form (e.g. Boolean, or System.Int64, etc). Those dynamic methods work on the the instance of the class. The ToString method on instances of System.Int32 converts the value into a nicely formatted string, with the help of a CultureInfo object, like this:

image

Static Methods and Static Properties

One concept that many new-to-PowerShell folks find harder to understand is concept of static (vs dynamic of instance) methods and properties. A static method is a method that does not make use of a specific instance of the class. Instead it works by doing something class related. The System.Int32 class has a neat static method, Parse. The Parse method takes a string and either parses that string into a 32-bit number or raises an exception if it can't correctly parse the string.  The PowerShell Syntax to invoke a static method is: [<class name>]::methodname(<array of method parameters, or nothing>]. For example:

[System.Int32]::parse('42')

The System.Int32 class also has static properties or properties of the class. For example, MaxValue is the largest value that a System.Int32 can hold, while MinValue is the smallest value. In PowerShell, the Syntax for static properties is: [<class name>]::<propertyname>.

Here is an illustration of static methods and properties.

image

As you can see in this screen shot, the Parse method parses nicely the string '42' into a 32-bit integer. But the string ('4223452354234523452345235234'') could not be parsed and the Parse method threw an exception, the Overflow exception, as you can . You could use the Parse method inside a Try/Catch block to, for example, validate that some value passed in from a user is of the right form (i.e. a 32-bit integer) and catch and handle any exceptions to that validation.

A static property is some property related to the class not an instance. System.Int32 also also has some static properties, including MaxValue (the largest 32-bit integer), and MinValue (the smallest integer).

Creating Object Instances

PowerShell users typically create the object instance by using cmdlets (or functions, aka script cmdlets). Thus you use Get-ChildItem cmdlet and get instances of System.Io.FileInfo and/or System.Io.DirectoryInfo classes. In most cases, you process the objects instances by using cmdlets for example using Set-ADUser to update user information in Active Directory. Best practice says to use cmdlets where you can and only then drop down into lower level code.

There are three ways you can create new instances of an object (class). The first is via a cmdlet, but in the absence of cmdlets/functions, then you can either  use a static method of the class or use the New-Object cmdlet and specify the class of which you wish to create an instance. One example is the  class System.Guid, which it defines and manages guids. There are no cmdlets to manage this object type. If there had been, then you would most likely have a New-Guid cmdlet to create a new guid. Since that cmdlet does not exist, to create a new guid, you use a static method, like this:

.

image

The other way to create new instances is to use the New-Object cmdlet. This cmdlet uses the class constructors available and matches the parameters you specify with the various constructors. For example, you can create a new instance of the System.Mail.Mailmessage class using one of four constructors. To Create an empty mail message, you would do this:

# Create an empty massage
$EmptyMsg = New-Object System.Net.Mail.Mailmessage

# Create a message from and to
$To= 'tfl@psp.co.uk'
$Fm='doctordns@gmail.com'
$MailMsg = New-Object System.Net.Mail.MailMessage $Fm, $To

So What?

In Windows, Microsoft and other vendors have provided thousands of cmdlets, but as yet, there is not full coverage of all the objects in the .NET framework. Thus if you need to use objects of those types (belonging to some less well known .NET Class). A good example is localisation. If you want to present a price in say US$, Norwegian crowns and UK Pound sterling. Assuming you know how to use the ServiceEx web services, the script looks like this:

# Get Curency conversion rates
$From = 'GBP'
$Tous  = 'USD'
$Tono  = 'NOK'
$CurrencyConvertor = New-WebServiceProxy "
http://www.webservicex.net/CurrencyConvertor.asmx?WSDL"
$USrate = $currencyConvertor.ConversionRate($from,$tous)
$NOrate = $currencyConvertor.ConversionRate($from,$tono)

# Calculate price
$priceUK = 12345.669
$priceNO = $PriceUK * $NOrate
$priceUS = $PriceUK * $USrate

# Convert to nice strings
$usinfo = [System.Globalization.CultureInfo]::CreateSpecificCulture('en-us')
$Noinfo = [System.Globalization.CultureInfo]::CreateSpecificCulture('no-nb')

"The UK price is:  $($PriceUK.tostring('c'))"
"The NO price is:  $($priceNO.tostring('c',$noinfo))"
"The US price is:  $($priceUS.tostring('c',$usinfo))"

And the output looks like this:

image

Pretty easy, when you know how to manage .NET classes and can use XML web services. The .NET framework contains a wealth of classes that have value, depending what you are working with. Being able to just reach out and grab them, when there are no cmdlets, provides you with a lot of power. Plus, if you understand what is going on with objects, troubleshooting scripts becomes easier – if nothing else, the error messages may make a bit more sense.

In the next article I'll look at actually combining the information presented here with how to create these things using PowerShell V5.

 

3 comments:

Unknown said...

Good Article !
PowerShell 5.0 adding up more values. Now in WMF 5 we have class as keyword
Class Test
{
[int] $result
[void] Add ([int] $value1 , [int] $value2)
$result = $value1 + $value2
$result
}
$a = [Test]::new()
$a.Add(233,34)
$a

Enjoy PowerShell !

Unknown said...

It's on the roof top :)
PowerShell 5.0 has class keyword which makes DevOps happy

Class Calculator
{
[int] $result
[void] Add ([int]$value1 , [int]$value2)
{
$result = $value1 + $value2
$result
}
[void] Subtract([int]$value1 , [int] $value2)
{
$result = $value1 - $value2
$result
}
[Void] Modulus ()
}

$a = [Calculator]::new()
$a.Add(20,3)
$a
$a.Subtract(20,3)
$a

Enjoy PowerShell :)

Class in V5 is very help ful for writing DSC resources.

Thomas Lee said...

I know you can write classes in PowerShell - I am leading up to that. The first article defines the terminology, the second and (shortly the third) articles look at how you can achieve that today (well more or less) and the 4th will look at native classes.

Thanks for the contribution.