I would like to declare some integer constants in PowerShell.
Is there any good way to do that?
I would like to declare some integer constants in PowerShell.
Is there any good way to do that?
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.
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
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).
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.
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:
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.-Mean
to -Value
, for consistency with Set-Variable
.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
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 notareal -Value constant -Option Constant -PassThru
$var.GetType().GetField('_value', 'NonPublic, Instance').SetValue($var, 'nope')
$notareal
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'
}
PowerShell v5.0 should allow
[static] [int] $variable = 42
[static] [DateTime] $thisday
and the like.