我希望能够在我的一些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中推荐的吗?
答案 0 :(得分:103)
PowerShell的可扩展类型系统最初并不允许您创建具体类型,您可以根据参数的方式进行测试。如果您不需要该测试,那么您可以使用上述任何其他方法。
如果你想要一个你可以强制转换或输入类型的实际类型,就像在你的示例脚本中一样......如果没有在C#或VB.net中编写它就不能 。在PowerShell 2中,您可以使用“添加类型”命令来完成它:
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-Type
或New-Struct
可让您在param([Contact]$contact)
中对该课程进行实际测试,并使用$contact = new-object Contact
制作新课程等等...
如果您不需要可以投射到的“真实”课程,则不必使用上面Steven and others have demonstrated的添加成员方式。
从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中,一切都发生了变化,我们最终得到了class
和enum
作为定义类型的语言关键字(没有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"
}
答案 1 :(得分:57)
可以在PowerShell中创建自定义类型 Kirk Munro实际上有两篇很好的帖子,详细介绍了这个过程。
本书Windows PowerShell In Action by Manning还有一个代码示例,用于创建特定于域的语言来创建自定义类型。这本书非常出色,所以我真的推荐它。
如果您只是想快速完成上述操作,可以创建一个函数来创建自定义对象,如
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
}
答案 2 :(得分:16)
这是快捷方式:
$myPerson = "" | Select-Object First,Last,Phone
答案 3 :(得分:9)
Steven Murawski的答案很棒,但是我喜欢较短的(或者更简洁的选择对象而不是使用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
}
答案 4 :(得分:4)
您可以使用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中以便直接使用。
这种行为肯定是鼓励的,因为它允许脚本的其他脚本或部分与实际对象一起使用。
答案 5 :(得分:3)
以下是创建自定义类型并将其存储在集合中的硬路径。
$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
答案 6 :(得分:0)
这里还有一个选项,它使用与Jaykul提到的PSTypeName解决方案类似的思想(因此也需要PSv3或更高版本)。
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>
Update-TypeData -AppendPath .\Person.Types.ps1xml
$p = [PSCustomType]@{PSTypeName='StackOverflow.Example.Person'}
$p.Initialize('Anne', 'Droid')
$p | Format-Table -AutoSize
$p.SetGivenName('Dan')
$p | Format-Table -AutoSize
PS1XML
或Add-Member
添加的成员仅限于NoteProperty
,AliasProperty
,ScriptProperty
,CodeProperty
,ScriptMethod
,和CodeMethod
(或PropertySet
/ MemberSet
;尽管它们受到相同的限制)。所有这些属性都是只读的。ScriptMethod
,我们可以欺骗上述限制。例如。我们可以定义一个方法(例如Initialize
)来创建新属性,并为我们设置它们的值;从而确保我们的对象具有其他脚本运行所需的所有属性。SetGivenName
所示。这种方法并非在所有情况下都理想。但对于将类类行为添加到自定义类型很有用/可以与其他答案中提到的其他方法结合使用。例如。在现实世界中,我可能只会在PS1XML中定义FullName
属性,然后使用一个函数来创建具有所需值的对象,如下所示:
看看documentation或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