Beyond custom objects: Create a .NET class
- Details
- Written by June Blender
- Last Updated: 14 April 2016
- Created: 02 December 2014
- Hits: 15718
In a previous article, I used the Select-String cmdlet to capture the table of keywords and references from the about_language_keywords help topic and then used the new ConvertFrom-String cmdlet in the Windows PowerShell 5.0 preview to convert each row of the table into a custom object (PSCustomObject). The custom object worked quite well. I could search, sort, format, and use the objects in Windows PowerShell.
PS C:\> $k = $table | ConvertFrom-String -TemplateContent $template | Select-Object -Property Keyword, Reference PS C:\> $k | where Keyword -eq "Trap" Keyword Reference ------- --------- Trap about_Trap, about_Break, about_Try_Catch_Finally
But, it’s just a simple step from the generic custom object to a named dynamic .NET object that I can create, manage, and use in many different contexts. In this post, I’ll use the new class feature of the Windows PowerShell 5.0 preview to create a Keyword class and real Keyword objects.
The class feature is new and works somewhat differently in the September version of the preview ($PSVersionTable.PSVersion = 5.0.9814.0) than it does in the November version (5.0.9883.0), so it’s likely to continue to change until (and even after) the official release.
For background, see the release notes that come with the Windows PowerShell 5.0 preview and Windows PowerShell MVP Trevor Sullivan’s playful and informative blog post, Implementing a .NET Class in PowerShell v5, in which he creates a Beer class.
Custom Objects for Keywords
The ConvertFrom-String cmdlet returns custom objects with the properties that you specify, either by using the PropertyNames parameter or one of the template parameters, TemplateContent or TemplateFile.
To see the properties and method of the objects that it returns, pipe the output (in this case, saved in the $k variable) to the Get-Member cmdlet.
PS C:\> $k | Get-Member TypeName: Selected.System.Management.Automation.PSCustomObject Name MemberType Definition ---- ---------- ---------- Equals Method bool Equals(System.Object obj) GetHashCode Method int GetHashCode() GetType Method type GetType() ToString Method string ToString() Keyword NoteProperty System.String Keyword=Begin Reference NoteProperty System.String Reference=about_Functions, about_Functions_Advanced
The objects have the Keyword and Reference properties that we need, but the methods are pretty generic and not particularly useful.
And, beginning in Windows PowerShell 5.0, it’s very easy to create a .NET class dynamically.
Create a Keyword Class
To create a class, begin with the class keyword followed by the name of the class, which, in this case, is Keyword. The content of the class is enclosed in curly braces.
To add properties to the class, just create global variables. In this case, I’ve added Name and Reference properties. (Yes, it’s that easy.)
class Keyword { #Properties [String]$Name [String]$Reference } |
To allow people to create objects that are instances of the class, add at least one constructor.
A constructor is a special type of method that creates new objects. It has the same name as the class and it never returns anything (the return type is [Void], which means nothing. You can have multiple constructors, but each one has to take different numbers and types of parameters.
In the Keyword class, I added one constructor that has two string parameters, Keyword and Reference. The commands in the constructor assign the value of the Keyword parameter to the Name property of the object and the value of the Reference parameter to the Reference property of the object.
class Keyword { #Properties [String]$Name [String]$Reference #Constructor Keyword ( [String]$Keyword [String]$Reference ) { $this.Name = $Keyword $this.Reference = $Reference } } |
Unlike the output types of functions, which are just notes, the output types of methods are enforced. If the method doesn’t return the declared output type, the method generates an error.
The parentheses that surround the parameters are required, even when the method doesn’t take any parameters.
As we discussed in the section about constructors, all method parameters are mandatory and positional. Each method has only one parameter set. Methods in a class can have the same name, but if they do, they must take different types and numbers of parameters.
Did you notice the $this automatic variable? Like $_ in pipelines, $this refers to the current object. Inside a class, when you refer to a property of the current object, you must use the $this prefix, a dot, and the property name.
For example, to refer to the Name property of the current object, type:
$this.Name
|
Requiring the $this variable lets you use parameters with the same names as the properties. Windows PowerShell uses the $this to distinguish the property from the parameter. For example, I used $Reference as a property of the object and a parameter of the constructor. To assign the $Reference parameter value to the $Reference property, I type:
# Property = Parameter $this.Reference = $Reference |
Create Keyword Objects
Now that our class is complete, we can create Keyword objects. To create an object that is an instance of a .NET class, call the New method. (The New-Object cmdlet doesn’t yet work for dynamically created .NET classes.) New is a static method — a method of the class, not of any particular object in the class. All .NET objects have the New method, because they inherit it from the System.Object class.
To call a static method, type the class name in square brackets, two colons (with no intervening spaces), the method name, New, and a set of parentheses.
This code calls the New method of the Keyword class:
[Keyword]::New() |
Inside the parentheses, call the constructor. Enter a comma-separated list of the values (omit the names) of the constructor parameters. All constructor parameters are mandatory and positional, so pay attention to the order of the values.
The Keyword class constructor has $Keyword and $Reference string parameters (in that order). To create a keyword object for the “If” keyword, use a command like this one:
[Keyword]::New("If", "about_If") |
To convert a custom object that has Keyword and Reference properties to a Keyword object, use a command like this one. It pipes the custom objects in the $k variable to the ForEach-Object cmdlet, which calls the New static method of the Keyword class on each object and then stores the results in the $keywords variable.
$keywords = $k | ForEach-Object { [Keyword]::new($_.Keyword, $_.Reference) |
Let’s see the result. The display looks just like the custom objects.
PS C:\> $keywords Name Reference ---- --------- Begin about_Functions, about_Functions_Advanced Break about_Break, about_Trap Catch about_Try_Catch_Finally Continue about_Continue, about_Trap
But, when you pipe them to the Get-Member cmdlet, you can see that the type is Keyword.
PS C:\> $keywords | Get-Member TypeName: Keyword Name MemberType Definition ---- ---------- ---------- Equals Method bool Equals(System.Object obj) GetHashCode Method int GetHashCode() GetType Method type GetType() ToString Method string ToString() Name Property string Name {get;set;} Reference Property string Reference {get;set;}
The real value becomes evident when you add methods to the class.
Add a Method
The value of the Reference property of each keyword is a list of help topics that explain how the keyword is used in Windows PowerShell. Let’s add a GetHelp() method to the Keyword class that returns the first help topic in each reference set.
The syntax of a class method is as follows.
[OutputType] Name (Parameters) |
- OutputType. Enclose in square brackets the .NET type of the objects that the method returns. If the method doesn’t return anything, that is, if the output type is [Void], you can omit the output type, because Void is the default. It’s nice to include it anyway.Unlike the output types of functions, which are just notes, the output types of methods are enforced. If the method doesn’t return the declared output type, the method generates an error.
- Name. Specify the name of the method. Typically, method names are a single camel-cased string.
- Parameters. The parameters of a method are always mandatory and positional. Each method has only one parameter set. Methods in a class can have the same name as other methods, but if they do, they must take different types and numbers of parameters. The parentheses that surround the parameters are required, even when the method doesn’t take any parameters.
Here’s the code for the GetHelp() method of Keyword objects. It returns the first help topic in the Reference value as a single string. I used a Try-Catch block in case the help topic isn’t on the computer. In that case, it just returns an empty string.
[String] GetHelp() { [String]$firstRef = $this.Reference.Split(",").Trim() | Select-Object -First 1 try { return Get-Help $firstRef } catch { return "" } } |
Let’s run the new improved Get-LanguageKeywords.ps1 script that contains the class and save the results in the $keywords property. Now, when you pipe the Keyword objects in the $keywords variable to the Get-Member cmdlet, you can see the new GetHelp() method:
PS C:\> $keywords | Get-Member TypeName: Keyword Name MemberType Definition ---- ---------- ---------- Equals Method bool Equals(System.Object obj) GetHashCode Method int GetHashCode() GetHelp Method string GetHelp() GetType Method type GetType() ToString Method string ToString() Name Property string Name {get;set;} Reference Property string Reference {get;set;}
And, you can call the GetHelp() method of any Keyword object. In these commands, I get the Keyword object whose name equals “If” and call its GetHelp() method. The method returns the text of the about_If help topic.
PS C:\> $if = $keywords | where Name -eq "If" PS C:\> $if.GetHelp() TOPIC about_If SHORT DESCRIPTION Describes a language command you can use to run statement lists based on the results of one or more conditional tests. LONG DESCRIPTION You can use the If statement to run code blocks if a specified conditional test evaluates to true. You can also specify one or more ...
Summary
It’s just a short hop from the custom object with its generic methods to a Keyword object with specialized methods.
One word of caution. When you create a class in Windows PowerShell, it’s not saved in a library like the standard .NET classes, so it exists only in your session. To save your class, save it in a script or script module file.
June Blender is a technology evangelist at SAPIEN Technologies, Inc. You can reach her at This email address is being protected from spambots. You need JavaScript enabled to view it. or follow her on Twitter at @juneb_get_help.
Get-Keywords.ps1
You can copy the script code here or download a ZIP file from the SAPIEN Downloads site.
To download, sign in, in the left nav, click Sample Scripts, click Get-Keywords.ps1.zip, and in the top right corner, click Download.
<# .NOTES =========================================================================== Created with: SAPIEN Technologies, Inc., PowerShell Studio 2014 v4.1.74 Created on: 11/30/2014 12:10 AM Organization: SAPIEN Technologies, Inc. Contact: June Blender, This email address is being protected from spambots. You need JavaScript enabled to view it., @juneb_get_help Filename: Get-Keywords.ps1 =========================================================================== .SYNOPSIS Creates Keyword objects from the Keyword-Reference table in the about_Language_Keywords help topic. .DESCRIPTION The Get-Keywords.ps1 script gets the Keyword-Reference table from the about_Language_Keywords help topic. It uses the ConvertFrom-String cmdlet to convert each row of the table to a custom object, and returns the custom objects. Then it uses a Keyword class defined in the script to create Keyword objects. The Script takes no parameters, but it requires the about_Language_Keyword help topic and Windows PowerShell version 5.0. .INPUTS None .OUTPUTS Keyword .EXAMPLE $keywords = .\Get-LanguageKeyword.ps1 $keywords.Keyword.Contains("While") True $keywords.Keyword.Contains("Grapefruit") False $keywords | where Name -eq "Finally"| Format-Table -Autosize Keyword Reference ---- --------- Finally about_Try_Catch_Finally Get-Help ($keywords | where Name -eq "Finally").Reference #> class Keyword{ #Properties [String]$Name [String]$Reference #Constructors Keyword( [String]$Keyword, [String]$Reference ) { $this.Name = $Keyword $this.Reference = $Reference } #Methods [String] GetHelp() { [String]$firstRef = $this.Reference.Split(",").Trim() | Select-Object -First 1 try { return Get-Help $firstRef } catch { return "" } } } $template = @" {Keyword*:Begin} {Reference:about_Functions, about_Functions_Advanced} {Keyword*:Catch} {Reference:about_Try_Catch_Finally} {Keyword*:Exit} {Reference:Described in this topic.} {Keyword*:Workflow} {Reference:about_Workflows} "@ if (!($help = dir $pshome\en-us\about_language_keywords.help.txt)) { throw "Can't find the about_language_keywords help topic. Run Update-Help and try again." } #Get the line number of the Keyword-Reference table header #Add 2 to get the line number of the first entry in the table $start = (($help | Select-String -Pattern 'Keyword\s{7,}Reference').LineNumber) + 2 #Get the line number of the first blank line after the table #Subtract one to get the line number of the last table entry $end = $start + (($help[($start - 1) .. 999] | Select-string -Pattern '^\s*$').LineNumber | Select-Object -First 1) - 1 #Get only the Keyword-Reference table #Subtract 1 from each line number to get the indexes $table = $help[($start - 1) .. ($end - 1)] #Create a custom object from the Keyword-Reference table $k = $table | ConvertFrom-String -TemplateContent $template | Select-Object -Property Keyword, Reference #Convert each custom object to a Keyword object #Use the null constructor and a hash table $keywords = $k | foreach { [Keyword]@{ Name = $_.Keyword; Reference = $_.Reference } }
For licensed customers, use the forum associated with your product in our Product Support Forums for Registered Customers.
For users of trial versions, please post in our Former and Future Customers - Questions forum.