How do I create a custom type in PowerShell for my scripts to use?

后端 未结 8 1221
遥遥无期
遥遥无期 2020-12-22 18:29

I would like to be able to define and use a custom type in some of my PowerShell scripts. For example, let\'s pretend I had a need for an object that had the following struc

相关标签:
8条回答
  • 2020-12-22 19:18

    Creating custom types can be done in PowerShell.
    Kirk Munro actually has two great posts that detail the process thoroughly.

    • Naming Custom Objects
    • Defining Default Properties for Custom Objects

    The book Windows PowerShell In Action by Manning also has a code sample for creating a domain specific language to create custom types. The book is excellent all around, so I really recommend it.

    If you are just looking for a quick way to do the above, you could create a function to create the custom object like

    function New-Person()
    {
      param ($FirstName, $LastName, $Phone)
    
      $person = new-object PSObject
    
      $person | add-member -type NoteProperty -Name First -Value $FirstName
      $person | add-member -type NoteProperty -Name Last -Value $LastName
      $person | add-member -type NoteProperty -Name Phone -Value $Phone
    
      return $person
    }
    
    0 讨论(0)
  • 2020-12-22 19:18

    Here's one more option, which uses a similar idea to the PSTypeName solution mentioned by Jaykul (and thus also requires PSv3 or above).

    Example

    1. Create a TypeName.Types.ps1xml file defining your type. E.g. Person.Types.ps1xml:
    <?xml version="1.0" encoding="utf-8" ?>
    <Types>
      <Type>
        <Name>StackOverflow.Example.Person</Name>
        <Members>
          <ScriptMethod>
            <Name>Initialize</Name>
            <Script>
                Param (
                    [Parameter(Mandatory = $true)]
                    [string]$GivenName
                    ,
                    [Parameter(Mandatory = $true)]
                    [string]$Surname
                )
                $this | Add-Member -MemberType 'NoteProperty' -Name 'GivenName' -Value $GivenName
                $this | Add-Member -MemberType 'NoteProperty' -Name 'Surname' -Value $Surname
            </Script>
          </ScriptMethod>
          <ScriptMethod>
            <Name>SetGivenName</Name>
            <Script>
                Param (
                    [Parameter(Mandatory = $true)]
                    [string]$GivenName
                )
                $this | Add-Member -MemberType 'NoteProperty' -Name 'GivenName' -Value $GivenName -Force
            </Script>
          </ScriptMethod>
          <ScriptProperty>
            <Name>FullName</Name>
            <GetScriptBlock>'{0} {1}' -f $this.GivenName, $this.Surname</GetScriptBlock>
          </ScriptProperty>
          <!-- include properties under here if we don't want them to be visible by default
          <MemberSet>
            <Name>PSStandardMembers</Name>
            <Members>
            </Members>
          </MemberSet>
          -->
        </Members>
      </Type>
    </Types>
    
    1. Import your type: Update-TypeData -AppendPath .\Person.Types.ps1xml
    2. Create an object of your custom type: $p = [PSCustomType]@{PSTypeName='StackOverflow.Example.Person'}
    3. Initialise your type using the script method you defined in the XML: $p.Initialize('Anne', 'Droid')
    4. Look at it; you'll see all properties defined: $p | Format-Table -AutoSize
    5. Type calling a mutator to update a property's value: $p.SetGivenName('Dan')
    6. Look at it again to see the updated value: $p | Format-Table -AutoSize

    Explanation

    • The PS1XML file allows you to define custom properties on types.
    • It is not restricted to .net types as the documentation implies; so you can put what you like in '/Types/Type/Name' any object created with a matching 'PSTypeName' will inherit the members defined for this type.
    • Members added through PS1XML or Add-Member are restricted to NoteProperty, AliasProperty, ScriptProperty, CodeProperty, ScriptMethod, and CodeMethod (or PropertySet/MemberSet; though those are subject to the same restrictions). All of these properties are read only.
    • By defining a ScriptMethod we can cheat the above restriction. E.g. We can define a method (e.g. Initialize) which creates new properties, setting their values for us; thus ensuring our object has all the properties we need for our other scripts to work.
    • We can use this same trick to allow the properties to be updatable (albeit via method rather than direct assignment), as shown in the example's SetGivenName.

    This approach isn't ideal for all scenarios; but is useful for adding class-like behaviors to custom types / can be used in conjunction with other methods mentioned in the other answers. E.g. in the real world I'd probably only define the FullName property in the PS1XML, then use a function to create the object with the required values, like so:

    More Info

    Take a look at the documentation, or the OOTB type file Get-Content $PSHome\types.ps1xml for inspiration.

    # have something like this defined in my script so we only try to import the definition once.
    # the surrounding if statement may be useful if we're dot sourcing the script in an existing 
    # session / running in ISE / something like that
    if (!(Get-TypeData 'StackOverflow.Example.Person')) {
        Update-TypeData '.\Person.Types.ps1xml'
    }
    
    # have a function to create my objects with all required parameters
    # creating them from the hash table means they're PROPERties; i.e. updatable without calling a 
    # setter method (note: recall I said above that in this scenario I'd remove their definition 
    # from the PS1XML)
    function New-SOPerson {
        [CmdletBinding()]
        [OutputType('StackOverflow.Example.Person')]
        Param (
            [Parameter(Mandatory)]
            [string]$GivenName
            ,
            [Parameter(Mandatory)]
            [string]$Surname
        )
        ([PSCustomObject][Ordered]@{
            PSTypeName = 'StackOverflow.Example.Person'
            GivenName = $GivenName
            Surname = $Surname
        })
    }
    
    # then use my new function to generate the new object
    $p = New-SOPerson -GivenName 'Simon' -Surname 'Borg'
    
    # and thanks to the type magic... FullName exists :)
    Write-Information "$($p.FullName) was created successfully!" -InformationAction Continue
    
    0 讨论(0)
提交回复
热议问题