如何在PowerShell中创建自定义类型以供我的脚本使用?

powershell scripting

69777 观看

8回复

13189 作者的声誉

我希望能够在我的一些PowerShell脚本中定义和使用自定义类型。例如,让我假装我需要一个具有以下结构的对象:

Contact
{
    string First
    string Last
    string Phone
}

我将如何创建它,以便我可以在以下功能中使用它:

function PrintContact
{
    param( [Contact]$contact )
    "Customer Name is " + $contact.First + " " + $contact.Last
    "Customer Phone is " + $contact.Phone 
}

这样的事情是可能的,甚至在PowerShell中推荐?

作者: Scott Saad 的来源 发布者: 2008 年 9 月 12 日

回应 (8)


4

9411 作者的声誉

您可以使用PSObject和Add-Member的概念。

$contact = New-Object PSObject

$contact | Add-Member -memberType NoteProperty -name "First" -value "John"
$contact | Add-Member -memberType NoteProperty -name "Last" -value "Doe"
$contact | Add-Member -memberType NoteProperty -name "Phone" -value "123-4567"

输出如下:

[8] » $contact

First                                       Last                                       Phone
-----                                       ----                                       -----
John                                        Doe                                        123-4567

另一个替代方案(我知道)是在C#/ VB.NET中定义一个类型,并将该程序集加载到PowerShell中以便直接使用。

绝对鼓励这种行为,因为它允许脚本的其他脚本或部分与实际对象一起使用。

作者: David Mohundro 发布者: 12.09.2008 08:36

58

9615 作者的声誉

决定

可以在PowerShell中创建自定义类型。
Kirk Munro实际上有两个很好的帖子,详细介绍了这个过程。

Manning的Windows PowerShell In Action一书还提供了一个代码示例,用于创建特定于域的语言来创建自定义类型。这本书很好,所以我真的推荐它。

如果您只是想快速完成上述操作,可以创建一个函数来创建自定义对象

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
}
作者: Steven Murawski 发布者: 12.09.2008 09:13

16

27314 作者的声誉

这是快捷方式:

$myPerson = "" | Select-Object First,Last,Phone
作者: EBGreen 发布者: 14.09.2008 01:05

107

12292 作者的声誉

在PowerShell 3之前

PowerShell的可扩展类型系统最初并不允许您创建具体类型,您可以根据参数的方式进行测试。如果您不需要该测试,那么您可以使用上述任何其他方法。

如果你想要一个你可以强制转换的类型或类型检查的实际类型,就像在你的示例脚本中那样......如果不在 C#或VB.net中编写并编译它就无法完成。在PowerShell 2中,您可以使用“Add-Type”命令来完成它:

add-type @"
public struct contact {
   public string First;
   public string Last;
   public string Phone;
}
"@

历史记录:在PowerShell 1中,它更难。你必须手动使用CodeDom,PoshCode.org上有一个非常古老的函数 new-struct脚本,这将有所帮助。你的例子变成:

New-Struct Contact @{
    First=[string];
    Last=[string];
    Phone=[string];
}

使用Add-TypeNew-Struct将让你实际测试你的课程param([Contact]$contact)并使用$contact = new-object Contact等等制作新课程......

在PowerShell 3中

如果您不需要可以强制转换的“真实”类,则不必使用Steven和其他人在上面演示过的Add-Member方法。

从PowerShell 2开始,您可以为New-Object使用-Property参数:

$Contact = New-Object PSObject -Property @{ First=""; Last=""; Phone="" }

在PowerShell 3中,我们可以使用PSCustomObject加速器添加TypeName:

[PSCustomObject]@{
    PSTypeName = "Contact"
    First = $First
    Last = $Last
    Phone = $Phone
}

你仍然只获得一个对象,所以你应该创建一个New-Contact函数来确保每个对象都是相同的,但你现在可以通过使用PSTypeName属性修饰参数来轻松验证参数“是”这些类型之一:

function PrintContact
{
    param( [PSTypeName("Contact")]$contact )
    "Customer Name is " + $contact.First + " " + $contact.Last
    "Customer Phone is " + $contact.Phone 
}

在PowerShell 5中

在PowerShell中5一切都改变了,我们终于得到了classenum作为语言关键字定义类型(有没有struct,但没关系):

class Contact
{
    # Optionally, add attributes to prevent invalid values
    [ValidateNotNullOrEmpty()][string]$First
    [ValidateNotNullOrEmpty()][string]$Last
    [ValidateNotNullOrEmpty()][string]$Phone

    # optionally, have a constructor to 
    # force properties to be set:
    Contact($First, $Last, $Phone) {
       $this.First = $First
       $this.Last = $Last
       $this.Phone = $Phone
    }
}

我们还有一种新的方法来创建对象而不使用New-Object[Contact]::new()- 实际上,如果你保持你的类简单并且没有定义构造函数,你可以通过转换哈希表来创建对象(虽然没有构造函数,但是没有办法强制必须设置所有属性):

class Contact
{
    # Optionally, add attributes to prevent invalid values
    [ValidateNotNullOrEmpty()][string]$First
    [ValidateNotNullOrEmpty()][string]$Last
    [ValidateNotNullOrEmpty()][string]$Phone
}

$C = [Contact]@{
   First = "Joel"
   Last = "Bennett"
}
作者: Jaykul 发布者: 15.09.2008 08:36

9

795 作者的声誉

Steven Murawski的答案很棒,但我喜欢较短的(或者更简单的是更简洁的select-object而不是使用add-member语法):

function New-Person() {
  param ($FirstName, $LastName, $Phone)

  $person = new-object PSObject | select-object First, Last, Phone

  $person.First = $FirstName
  $person.Last = $LastName
  $person.Phone = $Phone

  return $person
}
作者: Nick Meldrum 发布者: 12.01.2011 10:11

3

31 作者的声誉

以下是创建自定义类型并将其存储在集合中的硬路径。

$Collection = @()

$Object = New-Object -TypeName PSObject
$Object.PsObject.TypeNames.Add('MyCustomType.Contact.Detail')
Add-Member -InputObject $Object -memberType NoteProperty -name "First" -value "John"
Add-Member -InputObject $Object -memberType NoteProperty -name "Last" -value "Doe"
Add-Member -InputObject $Object -memberType NoteProperty -name "Phone" -value "123-4567"
$Collection += $Object

$Object = New-Object -TypeName PSObject
$Object.PsObject.TypeNames.Add('MyCustomType.Contact.Detail')
Add-Member -InputObject $Object -memberType NoteProperty -name "First" -value "Jeanne"
Add-Member -InputObject $Object -memberType NoteProperty -name "Last" -value "Doe"
Add-Member -InputObject $Object -memberType NoteProperty -name "Phone" -value "765-4321"
$Collection += $Object

Write-Ouput -InputObject $Collection
作者: Florian JUDITH 发布者: 15.12.2014 08:03

5

1906 作者的声誉

惊讶没有人提到这个简单的选项(vs 3或更高版本)来创建自定义对象:

[PSCustomObject]@{
    First = $First
    Last = $Last
    Phone = $Phone
}

该类型将是PSCustomObject,但不是实际的自定义类型。但它可能是创建自定义对象的最简单方法。

作者: Benjamin Hubbard 发布者: 10.09.2015 03:56

0

15027 作者的声誉

这里的一个选择,它使用一个类似的想法通过提到的PSTypeName溶液Jaykul(并因此还需要PSv3或以上)。

  1. 创建一个TypeName .Types.ps1xml文件,定义您的类型。例如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. 导入你的类型: Update-TypeData -AppendPath .\Person.Types.ps1xml
  2. 创建自定义类型的对象: $p = [PSCustomType]@{PSTypeName='StackOverflow.Example.Person'}
  3. 使用您在XML中定义的脚本方法初始化您的类型: $p.Initialize('Anne', 'Droid')
  4. 看它; 你会看到定义的所有属性:$p | Format-Table -AutoSize
  5. 键入调用mutator来更新属性的值: $p.SetGivenName('Dan')
  6. 再看一遍,看看更新的值: $p | Format-Table -AutoSize

说明

  • PS1XML文件允许您在类型上定义自定义属性。
  • 它不限于.net类型,如文档所暗示的那样; 因此,您可以在“/ Types / Type / Name”中放置您喜欢的内容,使用匹配的“PSTypeName”创建的任何对象都将继承为此类型定义的成员。
  • 成员通过添加PS1XMLAdd-Member限制为NotePropertyAliasPropertyScriptPropertyCodePropertyScriptMethod,和CodeMethod(或PropertySet/ MemberSet;尽管这些都受到同样的限制)。所有这些属性都是只读的。
  • 通过定义ScriptMethod我们可以欺骗上述限制。例如,我们可以定义一个方法(例如Initialize),它创建新属性,为我们设置它们的值; 从而确保我们的对象具有我们的其他脚本所需的所有属性。
  • 我们可以使用同样的技巧来允许属性可更新(尽管通过方法而不是直接赋值),如示例中所示SetGivenName

这种方法并非适用于所有场景; 但是对于向自定义类型添加类类行为很有用/可以与其他答案中提到的其他方法结合使用。例如在现实世界中,我可能只FullName在PS1XML中定义属性,然后使用函数创建具有所需值的对象,如下所示:

更多信息

查看文档或OOTB类型文件Get-Content $PSHome\types.ps1xml以获取灵感。

# 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
作者: JohnLBevan 发布者: 13.08.2019 04:14
32x32