ABOUT_CLASSES_PROPERTIES Short description Describes how to define properties for PowerShell classes. Long description Properties are members of the class that contain data. Properties are declared as variables in the class scope. A property can be of any built-in type or an instance of another class. Classes can zero or more properties. Classes don't have a maximum property count. Class properties can have any number of attributes, including the hidden and static attributes. Every property definition must include a type for the property. You can define a default value for a property. Syntax Class properties use the following syntaxes: One-line syntax [[]...] [] $ [= ] Multiline syntax [[]...] [] $ [= ] Examples Example 1 - Minimal class properties The properties of the EXAMPLEPROJECT1 class use built-in types without any attributes or default values. class ExampleProject1 { [string] $Name [int] $Size [bool] $Completed [string] $Assignee [datetime] $StartDate [datetime] $EndDate [datetime] $DueDate } [ExampleProject1]::new() $null -eq ([ExampleProject1]::new()).Name Name : Size : 0 Completed : False StartDate : 1/1/0001 12:00:00 AM EndDate : 1/1/0001 12:00:00 AM DueDate : 1/1/0001 12:00:00 AM True The default value for the NAME and ASSIGNEE properties is $null because they're typed as strings, which is a reference type. The other properties have the default value for their defined type, because they're value type properties. For more information on the default values for properties, see Default property values. Example 2 - Class properties with custom types The properties for EXAMPLEPROJECT2 include a custom enumeration and class defined in PowerShell before the EXAMPLEPROJECT2 class. enum ProjectState { NotTriaged ReadyForWork Committed Blocked InProgress Done } class ProjectAssignee { [string] $DisplayName [string] $UserName [string] ToString() { return "$($this.DisplayName) ($($this.UserName))" } } class ExampleProject2 { [string] $Name [int] $Size [ProjectState] $State [ProjectAssignee] $Assignee [datetime] $StartDate [datetime] $EndDate [datetime] $DueDate } [ExampleProject2]@{ Name = 'Class Property Documentation' Size = 8 State = 'InProgress' Assignee = @{ DisplayName = 'Mikey Lombardi' UserName = 'michaeltlombardi' } StartDate = '2023-10-23' DueDate = '2023-10-27' } Name : Class Property Documentation Size : 8 State : InProgress Assignee : Mikey Lombardi (michaeltlombardi) StartDate : 10/23/2023 12:00:00 AM EndDate : 1/1/0001 12:00:00 AM DueDate : 10/27/2023 12:00:00 AM Example 3 - Class property with a validation attribute The EXAMPLEPROJECT3 class defines the SIZE property as an integer that must be greater than or equal to 0 and less than or equal to 16. It uses the VALIDATERANGE attribute to limit the value. class ExampleProject3 { [string] $Name [ValidateRange(0, 16)] [int] $Size [bool] $Completed [string] $Assignee [datetime] $StartDate [datetime] $EndDate [datetime] $DueDate } $project = [ExampleProject3]::new() $project Name : Size : 0 Completed : False Assignee : StartDate : 1/1/0001 12:00:00 AM EndDate : 1/1/0001 12:00:00 AM DueDate : 1/1/0001 12:00:00 AM When EXAMPLEPROJECT3 instantiates, the SIZE defaults to 0. Setting the property to a value within the valid range updates the value. $project.Size = 8 $project Name : Size : 8 Completed : False Assignee : StartDate : 1/1/0001 12:00:00 AM EndDate : 1/1/0001 12:00:00 AM DueDate : 1/1/0001 12:00:00 AM When SIZE is set to an invalid value outside the range, PowerShell raises an exception and the value isn't changed. $project.Size = 32 $project.Size = -1 $project Exception setting "Size": "The 32 argument is greater than the maximum allowed range of 16. Supply an argument that is less than or equal to 16 and then try the command again." At line:1 char:1 + $project.Size = 32 + ~~~~~~~~~~~~~~~~~~ + CategoryInfo : NotSpecified: (:) [], SetValueInvocationExc eption + FullyQualifiedErrorId : ExceptionWhenSetting Exception setting "Size": "The -1 argument is less than the minimum allowed range of 0. Supply an argument that is greater than or equal to 0 and then try the command again." At line:1 char:1 + $project.Size = -1 + ~~~~~~~~~~~~~~~~~~ + CategoryInfo : NotSpecified: (:) [], SetValueInvocationExc eption + FullyQualifiedErrorId : ExceptionWhenSetting Name : Size : 8 Completed : False Assignee : StartDate : 1/1/0001 12:00:00 AM EndDate : 1/1/0001 12:00:00 AM DueDate : 1/1/0001 12:00:00 AM Example 4 - Class property with an explicit default value The EXAMPLEPROJECT4 class defaults the value for the STARTDATE property to the current date. class ExampleProject4 { [string] $Name [int] $Size [bool] $Completed [string] $Assignee [datetime] $StartDate = (Get-Date).Date [datetime] $EndDate [datetime] $DueDate } [ExampleProject4]::new() [ExampleProject4]::new().StartDate -eq (Get-Date).Date Name : Size : 0 Completed : False Assignee : StartDate : 10/23/2023 12:00:00 AM EndDate : 1/1/0001 12:00:00 AM DueDate : 1/1/0001 12:00:00 AM True Example 5 - Hidden class property The GUID property of the EXAMPLEPROJECT5 class has the hidden keyword. The GUID property doesn't show in the default output for the class or in the list of properties returned by Get-Member. class ExampleProject5 { [string] $Name [int] $Size [bool] $Completed [string] $Assignee [datetime] $StartDate [datetime] $EndDate [datetime] $DueDate hidden [string] $Guid = (New-Guid).Guid } $project = [ExampleProject5]::new() "Project GUID: $($project.Guid)" $project $project | Get-Member -MemberType Properties | Format-Table Project GUID: c72cef84-057c-4649-8940-13490dcf72f0 Name : Size : 0 Completed : False Assignee : StartDate : 1/1/0001 12:00:00 AM EndDate : 1/1/0001 12:00:00 AM DueDate : 1/1/0001 12:00:00 AM TypeName: ExampleProject5 Name MemberType Definition ---- ---------- ---------- Assignee Property string Assignee {get;set;} Completed Property bool Completed {get;set;} DueDate Property datetime DueDate {get;set;} EndDate Property datetime EndDate {get;set;} Name Property string Name {get;set;} Size Property int Size {get;set;} StartDate Property datetime StartDate {get;set;} Example 6 - Static class property The EXAMPLEPROJECT6 class defines the static PROJECTS property as a list of all created projects. The default constructor for the class adds the new instance to the list of projects. class ExampleProject6 { [string] $Name [int] $Size [bool] $Completed [string] $Assignee [datetime] $StartDate [datetime] $EndDate [datetime] $DueDate hidden [string] $Guid = (New-Guid).Guid static [ExampleProject6[]] $Projects = @() ExampleProject6() { [ExampleProject6]::Projects += $this } } "Project Count: $([ExampleProject6]::Projects.Count)" $project1 = [ExampleProject6]@{ Name = 'Project_1' } $project2 = [ExampleProject6]@{ Name = 'Project_2' } [ExampleProject6]::Projects | Select-Object -Property Name, Guid Project Count: 0 Name Guid ---- ---- Project_1 75e7c8a0-f8d1-433a-a5be-fd7249494694 Project_2 6c501be4-e68c-4df5-8fce-e49dd8366afe Example 7 - Defining a property in the constructor The EXAMPLEPROJECT7 class defines the DURATION script property in the static class constructor with the Update-TypeData cmdlet. Using the Update-TypeData or Add-Member cmdlet is the only way to define advanced properties for PowerShell classes. The DURATION property returns a value of $null unless both the STARTDATE and ENDDATE properties are set and STARTDATE is defined to be earlier than the ENDDATE. class ExampleProject7 { [string] $Name [int] $Size [bool] $Completed [string] $Assignee [datetime] $StartDate [datetime] $EndDate [datetime] $DueDate static [hashtable[]] $MemberDefinitions = @( @{ MemberName = 'Duration' MemberType = 'ScriptProperty' Value = { [datetime]$UnsetDate = 0 $StartNotSet = $this.StartDate -eq $UnsetDate $EndNotSet = $this.EndDate -eq $UnsetDate $StartAfterEnd = $this.StartDate -gt $this.EndDate if ($StartNotSet -or $EndNotSet -or $StartAfterEnd) { return $null } return $this.EndDate - $this.StartDate } } ) static ExampleProject7() { $TypeName = [ExampleProject7].Name foreach ($Definition in [ExampleProject7]::MemberDefinitions) { Update-TypeData -TypeName $TypeName @Definition } } ExampleProject7() {} ExampleProject7([string]$Name) { $this.Name = $Name } } $Project = [ExampleProject7]::new() $Project $null -eq $Project.Duration Duration : Name : Size : 0 Completed : False Assignee : StartDate : 1/1/0001 12:00:00 AM EndDate : 1/1/0001 12:00:00 AM DueDate : 1/1/0001 12:00:00 AM True The default view for an instance of the EXAMPLEPROJECT7 class includes the duration. Because the STARTDATE and ENDDATE properties aren't set, the DURATION property is $null. $Project.StartDate = '2023-01-01' $Project.EndDate = '2023-01-08' $Project Duration : 7.00:00:00 Name : Size : 0 Completed : False Assignee : StartDate : 1/1/2023 12:00:00 AM EndDate : 1/8/2023 12:00:00 AM DueDate : 1/1/0001 12:00:00 AM With the properties set correctly, the DURATION property returns a timespan representing how long the project ran. Default property values Every class property has an implicit default value depending on the type of the property. If a property is a reference type, like a string or an object, the implicit default value is $null. If a property is a value type, like a number, boolean, or enumeration, the property has a default value depending on the type: - Numeric types, like integers and floating-point numbers, default to 0 - Boolean values default to $false - Enumerations default to 0, even the enumeration doesn't define a label for 0. For more information about default values in .NET, see Default values of C# types (C# reference). To define an explicit default value for a property, declare the property with an assignment to the default value. For example, this definition for the PROJECTTASK class defines an explicit default value for the GUID property, assigning a random GUID to each new instance. class ProjectTask { [string] $Name [string] $Description [string] $Guid = (New-Guid).Guid } [ProjectTask]::new() Name Description Guid ---- ----------- ---- aa96350c-358d-465c-96d1-a49949219eec Hidden and static properties can also have default values. Hidden properties You can hide properties of a class by declaring them with the hidden keyword. Hidden class properties are: - Not included in the default output for the class. - Not included in the list of class members returned by the Get-Member cmdlet. To show hidden properties with Get-Member, use the FORCE parameter. - Not displayed in tab completion or IntelliSense unless the completion occurs in the class that defines the hidden property. - Public members of the class. They can be accessed and modified. Hiding a property doesn't make it private. It only hides the property as described in the previous points. For more information about the hidden keyword, see about_Hidden. Static properties You can define a property as belonging to the class itself instead of instances of the class by declaring the property with the static keyword. Static class properties: - Are always available, independent of class instantiation. - Are shared across all instances of the class. - Are always available. - Are modifiable. Static properties can be updated. They aren't immutable by default. - Live for the entire session span. [!IMPORTANT] Static properties for classes defined in PowerShell aren't immutable. They can Derived class properties When a class derives from a base class, it inherits the properties of the base class. Any properties defined on the base class, including hidden properties, are available on the derived class. A derived class can override an inherited property by redefining it in the class definition. The property on the derived class uses the redefined type and default value, if any. If the inherited property defined a default value and the redefined property doesn't, the inherited property has no default value. If a derived class doesn't override a static property, accessing the static property through the derived class accesses the static property of the base class. Modifying the property value through the derived class modifies the value on the base class. Any other derived class that doesn't override the static property also uses the value of the property on the base class. Updating the value of an inherited static property in a class that doesn't override the property might have unintended effects for classes derived from the same base class. The following example shows the behavior for static and instance properties on derived classes. class BaseClass { static [string] $StaticProperty = 'Static' [string] $InstanceProperty = 'Instance' } class DerivedClassA : BaseClass {} class DerivedClassB : BaseClass {} class DerivedClassC : DerivedClassB { [string] $InstanceProperty } class DerivedClassD : BaseClass { static [string] $StaticProperty = 'Override' [string] $InstanceProperty = 'Override' } "Base instance => $([BaseClass]::new().InstanceProperty)" "Derived instance A => $([DerivedClassA]::new().InstanceProperty)" "Derived instance B => $([DerivedClassB]::new().InstanceProperty)" "Derived instance C => $([DerivedClassC]::new().InstanceProperty)" "Derived instance D => $([DerivedClassD]::new().InstanceProperty)" Base instance => Instance Derived instance A => Instance Derived instance B => Instance Derived instance C => Derived instance D => Override The INSTANCEPROPERTY for DERIVEDCLASSC is an empty string because the class redefined the property without setting a default value. For DERIVEDCLASSD the value is Override because the class redefined the property with that string as the default value. "Base static => $([BaseClass]::StaticProperty)" "Derived static A => $([DerivedClassA]::StaticProperty)" "Derived static B => $([DerivedClassB]::StaticProperty)" "Derived static C => $([DerivedClassC]::StaticProperty)" "Derived static D => $([DerivedClassD]::StaticProperty)" Base static => Static Derived static A => Static Derived static B => Static Derived static C => Static Derived static D => Override Except for DERIVEDCLASSD, the value of the static property for the derived classes is the same as the base class, because they don't redefine the property. This applies even to DERIVEDCLASSC, which inherits from DERIVEDCLASSB instead of directly from BASECLASS. [DerivedClassA]::StaticProperty = 'Updated from A' "Base static => $([BaseClass]::StaticProperty)" "Derived static A => $([DerivedClassA]::StaticProperty)" "Derived static B => $([DerivedClassB]::StaticProperty)" "Derived static C => $([DerivedClassC]::StaticProperty)" "Derived static D => $([DerivedClassD]::StaticProperty)" Base static => Updated from A Derived static A => Updated from A Derived static B => Updated from A Derived static C => Updated from A Derived static D => Override When STATICPROPERTY is accessed and modified through DERIVEDCLASSA, the changed value affects every class except for DERIVEDCLASSD. For more information about class inheritance, including a comprehensive example, see about_Classes_Inheritance. Using property attributes PowerShell includes several attribute classes that you can use to enhance data type information and validate the data assigned to a property. Validation attributes allow you to test that values given to properties meet defined requirements. Validation is triggered the moment that the value is assigned. For more information on available attributes, see about_Functions_Advanced_Parameters. Defining instance properties with Update-TypeData Beyond declaring properties directly in the class definition, you can define properties for instances of a class in the static constructor using the Update-TypeData cmdlet. Use this snippet as a starting point for the pattern. Replace the placeholder text in angle brackets as needed. class { static [hashtable[]] $MemberDefinitions = @( @{ MemberName = '' MemberType = '' Value = } ) static () { $TypeName = [].Name foreach ($Definition in []::MemberDefinitions) { Update-TypeData -TypeName $TypeName @Definition } } } [!TIP] The Add-Member cmdlet can add properties and methods to a class in non-static constructors, but the cmdlet is run every time the constructor is called. Using Update-TypeData in the static constructor ensures that the code for adding the members to the class only needs to run once in a session. Only add properties to the class in non-static constructors when they can't be defined with Update-TypeData, like read-only properties. Defining alias properties The ALIAS attribute has no effect when used on a class property declaration. PowerShell only uses that attribute to define aliases for cmdlet, parameter, and function names. To define an alias for a class property, use Update-TypeData with the AliasProperty MEMBERTYPE. For example, this definition of the OPERABLEPAIR class defines two integer properties X and Y with the aliases LEFTHANDSIDE and RIGHTHANDSIDE respectively. class OperablePair { [int] $x [int] $y static [hashtable[]] $MemberDefinitions = @( @{ MemberType = 'AliasProperty' MemberName = 'LeftHandSide' Value = 'x' } @{ MemberType = 'AliasProperty' MemberName = 'RightHandSide' Value = 'y' } ) static OperablePair() { $TypeName = [OperablePair].Name foreach ($Definition in [OperablePair]::MemberDefinitions) { Update-TypeData -TypeName $TypeName @Definition } } OperablePair() {} OperablePair([int]$x, [int]$y) { $this.x = $x $this.y = $y } # Math methods for the pair of values [int] GetSum() { return $this.x + $this.y } [int] GetProduct() { return $this.x * $this.y } [int] GetDifference() { return $this.x - $this.y } [float] GetQuotient() { return $this.x / $this.y } [int] GetModulus() { return $this.x % $this.y } } With the aliases defined, users can access the properties with either name. $pair = [OperablePair]@{ x = 8 ; RightHandSide = 3 } "$($pair.x) % $($pair.y) = $($pair.GetModulus())" $pair.LeftHandSide = 3 $pair.RightHandSide = 2 "$($pair.x) x $($pair.y) = $($pair.GetProduct())" 8 % 3 = 2 3 x 2 = 6 Defining calculated properties To define a property that references the values of other properties, use the Update-TypeData cmdlet with the ScriptProperty MEMBERTYPE. For example, this definition of the BUDGET class defines the EXPENSES and REVENUES properties as arrays of floating-point numbers. It uses the Update-TypeData cmdlet to define calculated properties for total expenses, total revenues, and net income. class Budget { [float[]] $Expenses [float[]] $Revenues static [hashtable[]] $MemberDefinitions = @( @{ MemberType = 'ScriptProperty' MemberName = 'TotalExpenses' Value = { ($this.Expenses | Measure-Object -Sum).Sum } } @{ MemberType = 'ScriptProperty' MemberName = 'TotalRevenues' Value = { ($this.Revenues | Measure-Object -Sum).Sum } } @{ MemberType = 'ScriptProperty' MemberName = 'NetIncome' Value = { $this.TotalRevenues - $this.TotalExpenses } } ) static Budget() { $TypeName = [Budget].Name foreach ($Definition in [Budget]::MemberDefinitions) { Update-TypeData -TypeName $TypeName @Definition } } Budget() {} Budget($Expenses, $Revenues) { $this.Expenses = $Expenses $this.Revenues = $Revenues } } [Budget]::new() [Budget]@{ Expenses = @(2500, 1931, 3700) Revenues = @(2400, 2100, 4150) } TotalExpenses : 0 TotalRevenues : 0 NetIncome : 0 Expenses : Revenues : TotalExpenses : 8131 TotalRevenues : 8650 NetIncome : 519 Expenses : {2500, 1931, 3700} Revenues : {2400, 2100, 4150} Defining properties with custom get and set logic PowerShell class properties can't define custom getter and setter logic directly. You can approximate this functionality by defining a backing property with the hidden keyword and using Update-TypeData to define a visible property with custom logic for getting and setting the value. By convention, define the hidden backing property name with an underscore prefix and use camel casing. For example, instead of TaskCount, name the hidden backing property _taskCount. In this example, the PROJECTSIZE class defines a hidden integer property named _VALUE. It defines VALUE as a ScriptProperty with custom logic for getting and setting the _VALUE property. The setter scriptblock handles converting the string representation of the project to the correct size. class ProjectSize { hidden [ValidateSet(0, 1, 2, 3)] [int] $_value static [hashtable[]] $MemberDefinitions = @( @{ MemberType = 'ScriptProperty' MemberName = 'Value' Value = { $this._value } # Getter SecondValue = { # Setter $ProposedValue = $args[0] if ($ProposedValue -is [string]) { switch ($ProposedValue) { 'Small' { $this._value = 1 ; break } 'Medium' { $this._value = 2 ; break } 'Large' { $this._value = 3 ; break } default { throw "Unknown size '$ProposedValue'" } } } else { $this._value = $ProposedValue } } } ) static ProjectSize() { $TypeName = [ProjectSize].Name foreach ($Definition in [ProjectSize]::MemberDefinitions) { Update-TypeData -TypeName $TypeName @Definition } } ProjectSize() {} ProjectSize([int]$Size) { $this.Value = $Size } ProjectSize([string]$Size) { $this.Value = $Size } [string] ToString() { $Output = switch ($this._value) { 1 { 'Small' } 2 { 'Medium' } 3 { 'Large' } default { 'Undefined' } } return $Output } } With the custom getter and setter defined, you can set the VALUE property as either an integer or string. $size = [ProjectSize]::new() "The initial size is: $($size._value), $size" $size.Value = 1 "The defined size is: $($size._value), $size" $Size.Value += 1 "The updated size is: $($size._value), $size" $Size.Value = 'Large' "The final size is: $($size._value), $size" The initial size is: 0, Undefined The defined size is: 1, Small The updated size is: 2, Medium The final size is: 3, Large Limitations PowerShell class properties have the following limitations: - Static properties are always mutable. PowerShell classes can't define immutable static properties. Workaround: None. - Properties can't use the VALIDATESCRIPT attribute, because class property attribute arguments must be constants. Workaround: Define a class that inherits from the VALIDATEARGUMENTSATTRIBUTE type and use that attribute instead. - Directly declared properties can't define custom getter and setter implementations. Workaround: Define a hidden property and use Update-TypeData to define the visible getter and setter logic. - Properties can't use the ALIAS attribute. The attribute only applies to parameters, cmdlets, and functions. Workaround: Use the Update-TypeData cmdlet to define aliases in the class constructors. - When a PowerShell class is converted to JSON with the ConvertTo-Json cmdlet, the output JSON includes all hidden properties and their values. Workaround: None See also - about_Classes - about_Classes_Constructors - about_Classes_Inheritance - about_Classes_Methods