User Rating: 5 / 5

Star ActiveStar ActiveStar ActiveStar ActiveStar Active
 

The Return keyword in PowerShell isn't particularly well understood. It's explained in about_Return, but it's pretty obscure. And, to complicate matters, the Return keyword works very differently in PowerShell classes. This post describes how Return works in standard PowerShell and PowerShell classes.

Return Keyword in Standard PowerShell

Let's start with this Test-Return function. It gets the PowerShell processes on the system. Then it writes a string, then it returns 10, then it writes another string. Note that it has an output type of the function is hash table.

What do you think happens when you run the function? Specifically, what will the Return keyword return?

# How does 'return' work in a function?
function Test-Return {
{
    [OutputType([System.Collections.Hashtable])]
    param ()

    Get-Process PowerShell
    'This is a string.'
    return 10
    'Here is another string.'
}

Experienced PowerShell folks know the answer, but it startles newbies, and it makes programmers experienced in other languages shake their heads sadly.

PS C:\> Test-Return                                               
                                                                           
Handles  NPM(K)    PM(K)      WS(K)     CPU(s)     Id  SI ProcessName      
-------  ------    -----      -----     ------     --  -- -----------      
    811      28    64912      71076       0.78  10216   1 powershell       
This is a string.                                                          
10

As described in about_Return, the Return keyword exits the current scope. That's why the command after the Return statement ("Here is another string") doesn't run.

Return also returns the associated object (here, it's the integer 10), but it doesn't prevent the function from returning other things.

And, the OutputType that says the function returns a hash table? It's just information. It's not tested or verified. You can put anything in there and it's doesn't affect the results of the function.

Return Keyword in Classes

The Return keyword works very differently in methods in PowerShell classes. It works like the Return statements in other languages, including C#.

In a class method, the Return keyword:

  • Exits the current scope.
  • Returns the associated object (return <this>).
  • Returns ONLY the associated object. It prevents the method from returning anything else.
  • The object that Return returns must match the return type of the method.
    • A return statement with no return type is a syntax error.
    • A return type with no return statement is a syntax error.
    • Returning a different type from the return statement is a runtime error.

This is a bit more complex than the standard use, but it's consistent with the Return keyword and analogous keywords in other languages.

Let's try it. I've created a ReturnTester class with a TestReturn method. Other than being a method in a class, the TestReturn method is the same as the Test-Return function. But it works very differently.

What do you think happens when you run the method? Specifically, what will the Return keyword return?

class ReturnTester
{
    [Int32]TestReturn ()
    {
	Get-Process PowerShell
	'This is a string.'
        return 10
        'Here is another string.'
    }
}

To test, I'll create a ReturnTester object and save it in the $rt variable.

PS C:\> $rt = New-Object -TypeName ReturnTester

Now, I'll run the TestReturn method.

PS C:\ > $rt.TestReturn()

10

In a PowerShell class method, Return exits and current scope and returns ONLY the associated object. The other code still runs, but it's not returned.

When you remove the Return statement, the method doesn't return anything. The code still runs, but it doesn't return any objects. To make this work, I need to delete the return type or change it to the default, which is [Void] (does not return any objects).

TIP: If you're new to classes, the return type is specified in square brackets immediately before (to the left of) the method name, such as [Void] in "[Void] TestReturn".

class ReturnTester
{
    [Void]TestReturn ()
    {
        Get-Process PowerShell
	'This is a string.'
	10
        'Here is another string.'
    }
}

To test, I'll create a ReturnTester object and save it in the $rt variable.

PS C:\> $rt = New-Object -TypeName ReturnTester

Now, I'll run the TestReturn method. No return statement; nothing returned.

PS C:\ > $rt.TestReturn()
PS C:\>

Now, let's explore the relationship between the Return statement and the return type.

Unless the return type is omitted or [Void], the method must have a Return statement and it must return an object of the type specify by the return type. It's a contract, so no cheating. Let's test it.

First, we'll delete the Return keyword, but leave the return type as [int]. This causes a syntax error, because the function doesn't return the integer that the return type requires.

missingReturnKeyword

 

Now, let's try the opposite. We'll restore the Return keyword, but change the return type to [Void], which means that the method does not return any objects. This is equivalent to deleting the return type, which is valid, and uses the [Void] default. A [Void] return type causes a different syntax error; one that complains that you can't return something when the return type is Void.

missingReturnType

 

Now, we'll restore the return type to [int] but set the Return keyword to return a string. No syntax error and no error when you create an object, but you get a runtime error when you call the method and it actually runs.

This mismatch is declared only at runtime, because PowerShell is a loosely typed language that can convert an object from one type to another. The parser can't predict the conversion, so no error is declared until the engine truly fails to convert the string to an integer.

cantConvertStringToInt

 

One final example to show you that the parser is really strict. In this example, the "if ($True)" path always runs and the method always returns 10, which matches the [int] return type.

But, because this method has another code path (the Else statement) that doesn't return an integer, the parser rejects it. Even though the Else never runs. To fix it, you need to return an integer (typically -1) after writing the error.

allPathsMustReturn

 

So, the Return keyword behaves very differently in classes than it does in standard PowerShell.

When PowerShell was being developed, the team wanted to make it very simple to use and they thought that the standard Return was easier. But, it was so confusing to people who know Return from other languages, that they realized they had made a mistake. The implementation in classes is an attempt to rectify that mistake.

June Blender is a technology evangelist at SAPIEN Technologies, Inc. and a Windows PowerShell MVP. 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.

Thanks to Steve Mcknight (et al) for a great discussion about the Void structure. I have revised this article to indicate that Void means "returns no objects" not "nothing."

If you have questions about our products, please post in our support forum.
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 Trial Software Questions forum.
Copyright © 2017 SAPIEN Technologies, Inc.