Koans/Constructs/AboutCustomObjects.Koans.ps1
using module PSKoans [Koan(Position = 303)] param() <# Custom Objects PowerShell often makes extensive use of custom objects. Similar in usage to ExpandoObject for C#, powershell's PSObject wrapper allows almost any object to be given additional properties. These differ from their base type properties and are often in the form of NoteProperties and ScriptProperties. NoteProperties can store any value or reference to another object, permitting nested objects and effectively mirroring any static property as you might find on a defined class, but can be easily added to a new object without needing to define a class. ScriptProperties are defined as a script block value. Upon retrieving the value, the script block is evaluated, with the final value of the ScriptProperty being resolved on the completion of the script block. #> Describe '[PSCustomObject]' { It 'can be built from a hashtable' { <# This behaves like a cast, but it's actually a bit different. A directly-cast hashtable preserves the order of the elements, unlike regular hashtables, thanks to some parser magic. #> $Object = [PSCustomObject]@{ Property = 'Value' } $Object | Should -BeOfType PSCustomObject } It 'can be built by trimming objects down' { $Object = Get-ChildItem -Path $Home | Select-Object -First 1 -Property Name, Parent $Object | Should -BeOfType PSCustomObject __ | Should -Be $Object.Parent.Name } It 'can have arbitrary properties' { $Object = [PSCustomObject]@{ '__' = 'Enter Property Name' } __ | Should -Be $Object.PSObject.Properties.Count __.PSObject.Properties.Name | Should -Be 'PropertyName' } It 'can be added to' { $Object = [PSCustomObject]@{ 'Property1' = 12 } $Object | Add-Member -MemberType NoteProperty -Name 'Property2' -Value __ $Object.Property2 | Should -Be $($Object.Property1 - 7) } It 'can have ScriptProperties' { $Object = [PSCustomObject]@{ BaseProperty = 17 } $Object | Add-Member -MemberType ScriptProperty -Name DerivedProperty -Value { # ScriptProperties can reference their parent object using $this $this.BaseProperty++ $this.BaseProperty % 4 } __ | Should -Be $Object.DerivedProperty # What if we call it more than once? __ | Should -Be $Object.DerivedProperty } It 'can declare ScriptProperties without Add-Member with custom getters and setters' { $Object = [PSCustomObject]@{ BaseProperty = 11 } $Object.PSObject.Properties.Add( # A script property can consist of a name, a getter, and (optionally) a setter. [psscriptproperty]::new('CustomProperty', { # getter $this.BaseProperty + 1 }, { # setter param($val) $this.BaseProperty = - [int]($val) } ) ) __ | Should -Be $Object.CustomProperty $Object.CustomProperty = 12 __ | Should -Be $Object.CustomProperty __ | Should -Be $Object.BaseProperty } } |