如何初始化自定义对象的数组

首先,由于这引出了我的问题,我将首先指出,我已经在 PowerShell 中使用过 XML 很多次,并且喜欢如何快速地将 XML 文件中的数据读取到定制对象的数组中。例如,如果我有以下 XML 文件:

<stuff>
<item name="Joe" age="32">
<info>something about him</info>
</item>
<item name="Sue" age="29">
<info>something about her</info>
</item>
<item name="Cat" age="12">
<info>something else</info>
</item>
</stuff>

如果我简单地读一下,像这样:

[xml]$myxml = Get-Content .\my.xml

然后我可以得到一个项目数组,如下所示:

[array]$myitems = $myxml.stuff.Item
$myitems


name   age  info
----   ---  ----
Joe    32   something about him
Sue    29   something about her
Cat    12   something else

那么,现在我的问题是:

我如何创建一个类似的自定义对象数组结构,并初始化它们在我的脚本,没有读取一个文件?

我可以执行大量循环和/或大量创建/初始化单个对象,然后一次添加一个数组..。

但似乎应该有一种更简单的方法来执行这种创建/初始化。请注意,这里的关键是我的自定义对象有两个以上的元素(否则,我将使用散列)。

我甚至考虑过创建一个大的 XML 字符串,并使用 Select-XML,但是我就是不能正确地使用语法(如果这是正确的方法的话)。

212883 次浏览

也许你的意思是像这样? 我喜欢做一个对象,并使用格式表:

> $array = @()
> $object = New-Object -TypeName PSObject
> $object | Add-Member -Name 'Name' -MemberType Noteproperty -Value 'Joe'
> $object | Add-Member -Name 'Age' -MemberType Noteproperty -Value 32
> $object | Add-Member -Name 'Info' -MemberType Noteproperty -Value 'something about him'
> $array += $object
> $array | Format-Table


Name                                                                        Age Info
----                                                                        --- ----
Joe                                                                          32  something about him

这将根据对象的属性将数组中的所有对象放在列中。

提示: 使用 -auto大小的表更好

> $array | Format-Table -Auto


Name Age Info
---- --- ----
Joe   32 something about him

您还可以指定要在表中显示的属性。只需用逗号分隔每个属性名:

> $array | Format-Table Name, Age -Auto


Name Age
---- ---
Joe   32

我会这样做:

$myitems =
@([pscustomobject]@{name="Joe";age=32;info="something about him"},
[pscustomobject]@{name="Sue";age=29;info="something about her"},
[pscustomobject]@{name="Cat";age=12;info="something else"})

注意,这只适用于 PowerShell 3,但是由于您在问题中没有提到这个版本,我想这对您来说并不重要。

更新

评论中提到,如果你这样做:

$younger = $myitems | Where-Object { $_.age -lt 20 }
Write-Host "people younger than 20: $($younger.Length)"

你不会得到 1,因为你可能会期望。当返回单个 pscustomobject时会发生这种情况。对于 PowerShell 的大多数其他对象来说,这不是问题,因为它们具有 LengthCount的代理属性。不幸的是 pscustomobject没有。在 PowerShell 6.1.0中已经修复了这个问题。您可以使用操作符 @()来解决这个问题:

$younger = @($myitems | Where-Object { $_.age -lt 20 })

有关更多背景知识,请参见 给你给你

更新2

在 PowerShell 5中,人们可以使用 课程来实现类似的功能:

class Person {
[string]$name
[int]$age
[string]$info; `
`
Person(
[string]$name,
[int]$age,
[string]$info
){
$this.name = $name
$this.age = $age
$this.info = $info
}
}

这里的反勾是为了让您可以将其复制并粘贴到命令行,脚本中不需要它们。一旦定义了类,你就可以用通常的方法创建一个数组:

$myitems =@([Person]::new("Joe",32,"something about him"),
[Person]::new("Sue",29,"something about her"),
[Person]::new("Cat",12,"something else"))

注意,这种方法没有前面更新中提到的缺点,甚至在 PowerShell 5中也没有。

更新3

还可以使用散列表初始化类对象,类似于第一个示例。为此,您需要确保定义了默认构造函数。如果您没有提供任何构造函数,那么将为您创建一个构造函数,但是如果您提供了一个非默认的构造函数,那么将不会有缺省构造函数,您需要显式地定义它。下面是一个自动创建缺省构造函数的例子:

class Person {
[string]$name
[int]$age
[string]$info;
}

有了它,你可以:

$person = @([Person]@{name='Kevin';age=36;info="something about him"}
[Person]@{name='Sue';age=29;info="something about her"}
[Person]@{name='Cat';age=12;info="something else"})

这有点冗长,但也有点明确。

下面是在 PowerShell 中初始化散列表数组的简明方法。

> $body = @( @{ Prop1="1"; Prop2="2"; Prop3="3" }, @{ Prop1="1"; Prop2="2"; Prop3="3" } )
> $body


Name                           Value
----                           -----
Prop2                          2
Prop1                          1
Prop3                          3
Prop2                          2
Prop1                          1
Prop3                          3

下面是一个更简洁的可接受答案版本,它避免了重复 NoteProperty 标识符和 [pscustomobject]强制转换:

$myItems =  ("Joe",32,"something about him"), ("Sue",29,"something about her")
| ForEach-Object {[pscustomobject]@{name = $_[0]; age = $_[1]; info = $_[2]}}

结果:

> $myItems


name           age         info
----           ---         ----
Joe            32          something about him
Sue            29          something about her

使用“ Here-String”并转换为 XML。

[xml]$myxml = @"
<stuff>
<item name="Joe" age="32">
<info>something about him</info>
</item>
<item name="Sue" age="29">
<info>something about her</info>
</item>
<item name="Cat" age="12">
<info>something else</info>
</item>
</stuff>
"@


[array]$myitems = $myxml.stuff.Item


$myitems

初始化数组的最简单方法

创建数组

$array = @()

创建页眉

$line = "" | select name,age,phone

排好队

$line.name = "Leandro"
$line.age = "39"
$line.phone = "555-555555"

向 $array 添加行

$array += $line

结果

$array


name                                                     age                                                      phone
----                                                     ---                                                      -----
Leandro                                                  39                                                       555-555555

我必须创建一个预定义类型的数组,我成功地做到了以下几点:

[System.Data.DataColumn[]]$myitems = ([System.Data.DataColumn]("col1"),
[System.Data.DataColumn]("col2"),  [System.Data.DataColumn]("col3"))

考虑到上述数据,我会这样做:

# initialize the array
[PsObject[]]$people = @()


# populate the array with each object
$people += [PsObject]@{ Name = "Joe"; Age = 32; Info = "something about him" }
$people += [PsObject]@{ Name = "Sue"; Age = 29; Info = "something about her" }
$people += [PsObject]@{ Name = "Cat"; Age = 12; Info = "something else" }

下面的代码将工作,即使你只有一个项目后的 Where-Object:

# display all people
Write-Host "People:"
foreach($person in $people) {
Write-Host "  - Name: '$($person.Name)', Age: $($person.Age), Info: '$($person.Info)'"
}


# display with just 1 person (length will be empty if using 'PSCustomObject', so you have to wrap any results in a '@()' as described by Andrew Savinykh in his updated answer)
$youngerPeople = $people | Where-Object { $_.Age -lt 20 }
Write-Host "People younger than 20: $($youngerPeople.Length)"
foreach($youngerPerson in $youngerPeople) {
Write-Host "  - Name: '$($youngerPerson.Name)'"
}

结果:

People:
- Name: 'Joe', Age: 32, Info: 'something about him'
- Name: 'Sue', Age: 29, Info: 'something about her'
- Name: 'Cat', Age: 12, Info: 'something else'
People younger than 20: 1
- Name: 'Cat'

类的一个小变体。用散列表初始化它。

class Point { $x; $y }


$a = [Point[]] (@{ x=1; y=2 },@{ x=3; y=4 })


$a


x y
- -
1 2
3 4


$a.gettype()


IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     Point[]                                  System.Array


$a[0].gettype()


IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     False    Point                                    System.Object