164

I would like to declare some integer constants in PowerShell.

Is there any good way to do that?

0

7 Answers 7

171

Use

Set-Variable test -Option Constant -Value 100

or

Set-Variable test -Option ReadOnly -Value 100

The difference between "Constant" and "ReadOnly" is that a read-only variable can be removed (and then re-created) via

Remove-Variable test -Force

whereas a constant variable can't be removed (even with -Force).

See this TechNet article for more details.

Sign up to request clarification or add additional context in comments.

5 Comments

Hmm, but how do you force the datatype when using Set-Variable? When dealing with variables one may use [string]$name = value but that seems not to be possible for constants?
@masi just force the value Set-Variable test -option Constant -value [string]100
@Monso You need parentheses around the value when specifying type like ([string]100). See answers below.
This doesn't create a constant. As the variable cannot be used in validation sets. [ValidateSet($RunTypeStandard ,"Debug" ,"DebugWithEnvironment" )] I still get the error that "Attribute argument needs to be a constant"
Another difference between -Option Constant and -Option ReadOnly is, that you can apply ReadOnly to an existing variable, e. g. $CONST_FOO = 42; $CONST_BAR = 23; Set-Variable CONST_* -Option ReadOnly. I'm now using this scheme because it works better with code navigation. E. g. VSCode's PowerShell extension is able to navigate to the definition of a variable only if it is defined regularly ($var = value). By using a common prefix I only need to call Set-Variable once, which makes the code cleaner.
22

Here is a solution for defining a constant like this:

const myConst = 42

Solution taken from http://poshcode.org/4063

    function Set-Constant {
  <#
    .SYNOPSIS
        Creates constants.
    .DESCRIPTION
        This function can help you to create constants so easy as it possible.
        It works as keyword 'const' as such as in C#.
    .EXAMPLE
        PS C:\> Set-Constant a = 10
        PS C:\> $a += 13

        There is a integer constant declaration, so the second line return
        error.
    .EXAMPLE
        PS C:\> const str = "this is a constant string"

        You also can use word 'const' for constant declaration. There is a
        string constant named '$str' in this example.
    .LINK
        Set-Variable
        About_Functions_Advanced_Parameters
  #>
  [CmdletBinding()]
  param(
    [Parameter(Mandatory=$true, Position=0)]
    [string][ValidateNotNullOrEmpty()]$Name,

    [Parameter(Mandatory=$true, Position=1)]
    [char][ValidateSet("=")]$Link,

    [Parameter(Mandatory=$true, Position=2)]
    [object][ValidateNotNullOrEmpty()]$Mean,

    [Parameter(Mandatory=$false)]
    [string]$Surround = "script"
  )

  Set-Variable -n $name -val $mean -opt Constant -s $surround
}

Set-Alias const Set-Constant

1 Comment

Unfortunately this doesn't work when Set-Constant is contained in a module. It will create a constant in the module scope, where Set-Constant is contained. As a workaround one could pass parameter -Surround Global, but that's not always wanted. I would like to create a constant in another module or locally in a function.
18

To use a specific type of value, say Int64, you can explicitly cast the value used in set-variable.

For instance:

set-variable -name test -value ([int64]100) -option Constant

To check,

$test | gm

And you'll see that it is an Int64 (rather than Int32, which would be normal for the value 100).

2 Comments

This could be shortened to: set -o const test ([int64]100). This is one of the few cases where I would prefer the alias over the full command name in a script, to let my mind "scan" the source code quicker.
I don't agree. set -o is a bit too cryptic for me to have in a script.
16

I really like the syntactic sugar that rob's answer provides:

const myConst = 42

Unfortunately his solution doesn't work as expected when you define the Set-Constant function in a module. When called from outside the module, it will create a constant in the module scope, where Set-Constant is defined, instead of the caller's scope. This makes the constant invisible to the caller.

The following modified function fixes this problem. The solution is based on this answer to the question "Is there any way for a powershell module to get at its caller's scope?".

$null = New-Module {
    function Set-Constant {
        <#
        .SYNOPSIS
            Creates constants.
        .DESCRIPTION
            This function can help you to create constants so easy as it possible.
            It works as keyword 'const' as such as in C#.
        .EXAMPLE
            PS C:\> Set-Constant a = 10
            PS C:\> $a += 13

            There is a integer constant declaration, so the second line return
            error.
        .EXAMPLE
            PS C:\> const str = "this is a constant string"

            You also can use word 'const' for constant declaration. There is a
            string constant named '$str' in this example.
        .LINK
            Set-Variable
            About_Functions_Advanced_Parameters
        #>
        [CmdletBinding()]
        param(
            [Parameter(Mandatory, Position=0)] [string] $Name,
            [Parameter(Mandatory, Position=1)] [char] [ValidateSet('=')] $Link,
            [Parameter(Mandatory, Position=2)] [object] $Value
        )

        try {
            $PSCmdlet.SessionState.PSVariable.Set( 
                [Management.Automation.PSVariable]::new( 
                    $Name, $Value, [Management.Automation.ScopedItemOptions]::Constant ) )
        }
        catch {
            # This makes sure the location of the call to Set-Constant is reported 
            # in the error message, instead of the location of the call to PSVariable.Set().
            $PSCmdlet.WriteError( $_ )
        }
    }
}

Set-Alias const Set-Constant

Notes:

  • The New-Module line is there because the function only works, when called from a different scope domain (aka session state). You could put the function into an actual module file (.psm1), but then you couldn't use it from within that same module! The in-memory module makes it usable as-is from both PowerShell scripts (.ps1) as well as module files.
  • I've renamed the parameter -Mean to -Value, for consistency with Set-Variable.
  • The function could be extended to optionally set the Private, ReadOnly and AllScope flags. Simply add the desired values to the 3rd argument of the PSVariable constructor, which is called in the above script through New-Object.

Edit 06/2023:

A disadvantage of both Set-Constant and any solution based on Set-Variable -Option Constant is, that VSCode's PowerShell extension does not support navigation to the definition of the variable, e. g. by Ctrl+Click on the variable name (see this GitHub issue).

My current workaround is to define the constants like normal variables (so VSCode sees their definition in the AST) and then make them constant by redefining them using New-Variable -Force -Option Constant. Contrary to Set-Variable, the New-Variable command can overwrite existing variables.

A typical module that exports constants, now looks like this:

# Define variables
$CONST_FOO = 42
$CONST_BAR = 23

# Make the variables constant, by redefining them as constants.
Get-Variable CONST_* | ForEach-Object { New-Variable -Name $_.Name -Value $_.Value -Option Constant -Force }

# Export the constants from the module
Export-ModuleMember -Variable CONST_*

Here is a full demo to play around with:

$null = New-Module {
    $CONST_FOO = 42
    $CONST_BAR = 23
    
    Get-Variable CONST_* | ForEach-Object { New-Variable -Name $_.Name -Value $_.Value -Option Constant -Force }
    Export-ModuleMember -Variable CONST_*
}

# All of these should error out
$CONST_FOO = 0
$CONST_BAR = 0
Remove-Variable CONST_FOO -Force

2 Comments

You could check the call stack to find out where the assignment took place: if ((Get-PSCallStack)[1].Command -eq 'your-module-file-name.psm1') { <# inside module #> } else { <# outside #> }
@pfeileon Found a different way using an in-memory module.
15

Use -option Constant with the Set-Variable cmdlet:

Set-Variable myvar -option Constant -value 100

Now $myvar has a constant value of 100 and cannot be modified.

5 Comments

Wow, that's clunky. You have to use Set-Variable to do it, huh?
Yes, there's not unclunky way to do it :)
you can also modify and existing variable with either set-variable (aliased to sv) or by using get-variable (gv) and tinkering with its Options property.
Hmm, but how do you force the datatype when using Set-Variable? When dealing with variables one may use [string]$name = value but that seems not to be possible for constants?
@masi - see Mike Shepard's answer elsewhere in this page. Copy and pasting from there, it's: set-variable -name test -value ([int64]100) -option Constant
3

There is really 0 benefit from creating a constant thru a psvariable. There is no performance gain and it can be updated via reflection at will:

$var = Set-Variable fakeConst -Value constant -Option Constant -PassThru

$var.GetType().
    GetField('_value', 'NonPublic, Instance').
    SetValue($var, 'nope')
$fakeConst

if ($IsCoreCLR) {
    $field = '_options'
}
else {
    $field = 'options'
}

$var.GetType().
    GetField($field, [System.Reflection.BindingFlags] 'NonPublic, Instance').
    SetValue($var, [System.Management.Automation.ScopedItemOptions]::None)
$fakeConst = 'not a constant anymore :('
$fakeConst

It's not a real constant. If you want a real const the way is thru inline C#:

$addTypeSplat = @{
    MemberDefinition = 'public const string Const = "myConstValue";'
    Name             = 'MyConstClass'
    Namespace        = 'MyNamespace'
}

Add-Type @addTypeSplat
[MyNamespace.MyConstClass]::Const

Or the closest you can get to it from PowerShell is with a static property in a PowerShell Class (from a performance standpoint):

class MyConst {
    static [string] $Const = 'constValue'
}

Comments

-7

PowerShell v5.0 should allow

[static] [int] $variable = 42

[static] [DateTime] $thisday

and the like.

2 Comments

does not work on ps 5.1. Unable to find type [static].
Not to mention, static is not the same as a constant

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.