Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to create a read-only property in a PowerShell (> 5.0) class

Tags:

powershell

With the introduction of PowerShell class since version 5.0, I was wondering if it is possible to create a read-only property and how to do it. The property value is populated from the internal logic and the user can read it but not set it.

Consider the following code to create a class and let's say I want to make the property "Year" as read-only :

class Car {
    [string] $Maker;
    [string] $Model;
    [string] $Year;
    [int] $Odometer ;


    [void] Drive([int] $NbOfKM) { 
        $this.Odometer += $NbOfKM;  
     }

    Car(){
    }

    Car([int] $Odometer){
        $this.$Odometer = $Odometer;
    }
}
like image 540
Martin Lebel Avatar asked Jan 15 '17 16:01

Martin Lebel


People also ask

Is a readonly property PowerShell?

Although Powershell doesn't have real readonly class properties, we can mimic them in two elegant ways: Class methods as getter and setter functions. Script properties with getter and setter functions.

How do you use properties in PowerShell?

Object properties To get the properties of an object, use the Get-Member cmdlet. For example, to get the properties of a FileInfo object, use the Get-ChildItem cmdlet to get the FileInfo object that represents a file. Then, use a pipeline operator ( | ) to send the FileInfo object to Get-Member .


2 Answers

Unfortunately there is no getter/setter support in PowerShell.

What you can do though is use the hidden keyword to hide the property and only provide a method to get the value, but only set it internally. Then the "setter" becomes less discoverable.

like image 164
Stuart Avatar answered Nov 15 '22 07:11

Stuart


I don't have enough reputation to comment on Stuart's answer. However, this builds on his answer, because the workaround is to override the setter method.

I've found that you can implement getters and setters in an inelegant way by using ScriptProperty. Two relevant posts: Powershell class implement get set property, https://powershell.org/forums/topic/class-in-powershell-5-that-run-custom-code-when-you-set-property/. I prefer the second, because it puts the dynamic code into the constructor. An example from my code:

class MyClass {
    MyClass () {
        $this.PSObject.Properties.Add(
            (New-Object PSScriptProperty 'ReadOnlyProperty', {$this._ROF})
        )
    }

    hidden [string]$_ROF = 'Readonly'
}

The constructor for PSScriptProperty takes a name and then one or two scriptblocks; the first is the getter, the second (if provided) is the setter. https://msdn.microsoft.com/en-us/library/system.management.automation.psscriptproperty(v=vs.85).aspx

Downsides of this approach:

  • You have to manually create your backing field (I called mine _ROF)
  • It's ugly to define your properties dynamically like this. You can't have a declaration of the property in the class body, because you are adding it dynamically (essentially, with Add-Member)
  • You end up with a ScriptProperty instead of a Property
  • If you try to assign to your read-only property, you get a SetWithoutSetterFromScriptProperty exception instead of a PropertyAssignmentException.
like image 40
FSCKur Avatar answered Nov 15 '22 05:11

FSCKur