Creating Functions from Code

By wrapping code into a function you make PowerShell code reusable. ISESteroids provides a number of wizards to help you quickly auto-generate functions, and below we’ll look at the first of them.

Start with some code

Let’s assume you found or created yourself a chunk of code that you find useful:

$Text = 'Hello I am PowerShell!'
$Rate = -2

$object = New-Object -ComObject Sapi.SpVoice
$object.Rate = $Rate
$null = $object.Speak($Text)

When you paste this code into the ISE editor and run it, it invokes the text-to-speech engine, and (provided you turned up the volume,) your computer starts talking to you when you run your script via F5.

Turn Code Into Function

To make this code reusable, select the entire code. Once code is selected, an icon is activated:

Image

Click this icon. You can also right-click on the selected code, and choose Selection/Auto-Create Function in the context menu that opens. A wizard dialog opens.

Define New Function

Take a careful look at the options in the wizard dialog:

  • In the upper part, select a new name for your function. Choose an approved verb from the dropdown box, and type a noun in the textbox to the right. The name for your new function could be Out-Voice.
  • In the lower part, the wizard lists all variables found in your selected code. Make sure you uncheck all variables that should be internal and not turned into user parameters. In our example, object needs to be unchecked because it is an internal variable, and a user should never have to change or even see this variable.

Image

Generally, keep the checkbox checked only for variables that a user would typically want to change.

Autogenerated Code

Once you click OK, ISESteroids auto-generates the function code for you and places it into a new tabbed editor:

function Out-Voice
{
  <#
    .SYNOPSIS
    Short Description
    .DESCRIPTION
    Detailed Description
    .EXAMPLE
    Out-Voice
    explains how to use the command
    can be multiple lines
    .EXAMPLE
    Out-Voice
    another example
    can have as many examples as you like
  #>
  [CmdletBinding()]
  param
  (
    [Parameter(Mandatory=$false, Position=0)]
    [System.String]
    $Text = 'Hello I am PowerShell!',
    
    [Parameter(Mandatory=$false, Position=1)]
    [System.Int32]
    $Rate = -2
  )
  
  $object = New-Object -ComObject Sapi.SpVoice
  $object.Rate = $Rate
  $null = $object.Speak($Text)
}

To actually define your function, send it to PowerShell by running the script: press F5, or click the green triangle “play” button. This defines your function, so it is now ready-to-use, but it won’t run it.

To run your function, click into the blue interactive console, and enter your function name. Intellisense picks it up, and your new function now extends the built-in PowerShell command set. Use Out-Voice like any other PowerShell command.

Try this:

Out-Voice -Text 'This is my text to speech generator!'

Or this:

Out-Voice -Text 'It even has a beer mode!' -Rate -10

Editing and Fine-Tuning

Your function code can now be edited and refined if you like.

Make sure you run your code whenever you have changed it! PowerShell won’t notice your edits until you actually run your script by pressing F5. If you don’t run it, all edits remain unnoticed and do not take effect.

Here are a couple of suggestions what you can do now to improve your function:

Providing Comment Based Help

Let’s make your function user-friendly by adding help. The comment based help block is already in place, and you just need to add some meaningful description and a couple of comments. For example:

<#
    .SYNOPSIS
    Sends text to the text-to-speech engine
    .DESCRIPTION
    Turns text to speech. Make sure you have turned up your volume!
    .EXAMPLE
    Out-Voice -Text "Hello World!"
    says: "Hello World!"
    .EXAMPLE
    Out-Voice -Text "I am a little drunk!" -Rate -10
    Talks in a very slow voice. -Rate can be any number between -10 and 10.
  #>

You are not just documenting your code, you are also directly defining the PowerShell help window content for your function! Check it out:

  1. In the editor, click anywhere on the function name (do not select anything)
  2. Press F1. The help window opens and displays the help information from the comment block including your examples.

Mandatory and Optional Parameters

You can decide whether your function parameters should be optional or mandatory:

  • Optional parameters can be omitted. When omitted, they use their default values, and when specified, they use the specified value
  • Mandatory parameters must be specified. They cannot have a default value. You define whether a parameter is optional or mandatory by using the Mandatory attribute. To make the Text parameter mandatory and keep the Rate optional with a default of 0, change the code like this:
[CmdletBinding()]
  param
  (
    [Parameter(Mandatory=$true, Position=0)]
    [System.String]
    $Text,
    
    [Parameter(Mandatory=$false, Position=1)]
    [System.Int32]
    $Rate = 0
  )

Don’t forget to run the code again. When you now run Out-Voice without specifying any argument, PowerShell prompts for the Text parameter because you declared it to be mandatory:

Cmdlet Out-Voice at command pipeline position 1
Supply values for the following parameters:
Text: 

You can now specify the text to turn into speech, or press CTRL+C to cancel.

Parameter Validators

Your parameters already specify a type, so they only accept values that can be converted into the desired type.

  • The Text parameter is of type String and accepts any text
  • The Rate parameter is of type Int (equivalent to Int32 or System.Int32 which are all the same) and accepts any whole number

That’s good but may not be sufficient. In our example, the text-to-speech engine allows Rate to be a number in the range of -10 to 10 only.

To prevent the user from submitting illegal values, you can add Validators that further restrict what a parameter accepts. For numeric ranges, use the [ValidateRange()] attribute like this:

[Parameter(Mandatory=$false, Position=1)]
[ValidateRange(-10,10)]
[System.Int32]
$Rate = 0

Once you run your code, your function will no longer accept values for Rate outside the range of -10 to 10.

Positional Parameters

Sometimes, users are too lazy to use parameter names, and they try and save some typing by omitting parameter names. Instead, they just submit their arguments in the expected order. While that is not a good practice, it is a common thing:

# well-formed, each argument is assigned to a parameter:
Out-Voice -Text 'This is my text to speech generator!' -Rate 5

# lazy mode: arguments are provided in the correct order but without parameter names:
Out-Voice 'This is my text to speech generator!' 5

The “lazy” positional mode is activated in either of these cases:

  • when there is a Position attribute per parameter (as is the case in our auto-generated code)
  • when there are no Position attributes at all in any of your parameters

So to use positional mode, you can either leave the auto-generated code as-is, or remove all Position attributes.

Fine-Tuning with PSSharper

PSSharper constantly analyzes your code in real-time and displays useful hints in the PSSharper Bar at the bottom of the editor:

Image

  • PSSharper provides visual clues right inside your code. For example, when you define a default value for parameters but the parameter is declared to be Mandatory and therefore will never ever use the default value, PSSharper strikes through the default value (see figure A).
  • Click a category (see figure B) to find out how your PowerShell code can be improved. This opens the PSSharper sidebar with a details view.
  • Click each entry in the PSSharper view (see figure C) to move to the corresponding code position. Double-click the entry to apply an automated fix. And click the “gear” symbol in front of the entry to find out why PSSharper is making the suggestion.

PSSharper makes it easy for beginners and seasoned experts alike to quickly go through most coding issues, making sure your code is safely adhering to all best practices.

Never apply a suggested fix if you don’t understand why it is suggested. PSSharper makes suggestions only. Whether they are appropriate is something for you to decide. While most suggestions make immediate sense, there are some that aim to improve performance or make other high-level code changes. Simply press CTRL+Z to undo any change you dislike.

Complete Example

If you have followed all examples and suggestions, the chunk of code from the beginning may now look like this:

function Out-Voice
{
  <#
    .SYNOPSIS
    Sends text to the text-to-speech engine
    .DESCRIPTION
    Turns text to speech. Make sure you have turned up your volume!
    .EXAMPLE
    Out-Voice -Text "Hello World!"
    says: "Hello World!"
    .EXAMPLE
    Out-Voice -Text "I am a little drunk!" -Rate -10
    Talks in a very slow voice. -Rate can be any number between -10 and 10.
  #>
  param
  (
    [Parameter(Mandatory,HelpMessage='Text to speak')]
    [string]
    $Text,
    
    [int]
    [ValidateRange(-10,10)]
    $Rate = -2
  )
  
  $object = New-Object -ComObject Sapi.SpVoice
  $object.Rate = $Rate
  $null = $object.Speak($Text)
}

Your function is now done, and you may be wondering how you can save it and turn it into a permanent PowerShell command. For now, save your script as a PowerShell script file. Whenever you want to use your new command Out-Voice, you would have to load the file into ISE and run it.

That’s why turning code into functions is just the first of two steps. The second step is saving functions into PowerShell modules so that PowerShell knows about your new function at all times and can run it whenever or whereever you need it. ISESteroids helps you auto-generate PowerShell modules in just a few additional clicks.