gpt4 book ai didi

arrays - Powershell阵列:何时使用它们;什么时候避免他们和使用它们的问题

转载 作者:行者123 更新时间:2023-12-01 15:01:35 27 4
gpt4 key购买 nike

.NET Framework ArrayList类.Add方法为什么在PowerShell实现中不起作用?

除非经过更正,否则我认为我的故事的总体寓意可能是:不要假定本机PowerShell方法将与.NET方法相同,并且在PowerShell中尝试.NET方法时要小心。

我寻求的原始解决方案是从函数(作为数组)以用户定义的日期范围作为参数返回日期列表。然后,将引用日期数组以移动和读取以日期戳命名的文件。

我遇到的第一个问题是创建动态数组。我不知道自己在做什么,并且在.Add数组声明上错误地调用了.NET @()方法。

Exception calling "Add" with "1" argument(s): "Collection was of a fixed size."



当我真正的问题是我做得不好时,我以为我需要找到一个动态数组类型。这使我朝着不同的方向前进,直到很久以后,我才发现应该使用 +=语法将对象添加到PowerShell数组。

无论如何,在我回到如何正确使用PowerShell数组之前,我没有使用其他切线。

然后,我找到了.NET ArrayList类。好的。现在,我有了一个动态数组对象。我阅读了文档,其中说我应该使用 .Add方法将元素添加到集合中。

然后,我开始寻求更深入的了解,因为我经历了几天的挫折,试图解决问题。

我制作了一个起初似乎可行的实现。它产生了一个日期范围-但它也产生了一些奇怪的行为。我观察到返回的日期很奇怪,例如:

Monday, January 1, 0001 12:00:00 AM



我发现,这是您执行以下操作时获得的结果:
Get-Date 0
ArrayList首先返回数组元素的索引值列表,然后返回数组值。那根本没有任何意义。我开始研究是否正确地调用了函数,是否遇到了某种可变范围的问题,或者我只是疯了。

我现在非常确信,我的沮丧是由于缺乏扎实的初学者引用而引起的,该引用不仅显示了如何进行简单数组实现的几个示例,而且还描​​述了一些注意事项以及其他解决方案。

然后,让我在这里解释实现数组/集合的三种方式,以及针对我尝试生成的内容的解决方案-即,日期范围内的日期列表。

由于某种原因,我最初认为在Powershell中将元素添加到.NET ArrayList的正确方法是使用.Add方法。这是 documented。我仍然不明白为什么该方法不起作用的原因(严重的是,请有人启发我)。通过实验,我发现我可以通过使用 +=方法将对象添加到ArrayList来获得准确的结果。

不要这样这绝对是错误的。它将产生我上面描述的错误:
Function Get-DateRangeList {
[cmdletbinding()]
Param (
[datetime] $startDate,
[datetime] $endDate
)

$datesArray = [System.Collections.ArrayList]@() # Second method

for ($d = $startDate; $d -le $endDate; $d = $d.AddDays(1)) {
if ($d.DayOfWeek -ne 'Sunday') {
$datesArray.Add($d)
}
}

Return $datesArray
}

# Get one week of dates, ending with yesterday's date
$startDate = Get-Date
$endDate = $startDate.AddDays(-1) # Get yesterday's date as last date in range
$startDate = $endDate.AddDays(-7) # Get 7th prior date as first date in range

$datesList = Get-DateRangeList $startDate $endDate

# Loop through the dates
Foreach ($d in $datesList) {
# Do something with each date, e.g., format the date as part of a list
# of date-stamped files to retrieve
$d
}

现在,下面有三个代码示例可以执行。在每个示例中,代码都是相同的。我所做的只是注释/取消注释了相应的实例化行和方法行。

首先,使用本机PowerShell数组对象:
Function Get-DateRangeList {
[cmdletbinding()]
Param (
[datetime] $startDate,
[datetime] $endDate
)

$datesArray = @() # First method
#$datesArray = [System.Collections.ArrayList]@() # Second method
#$datesArray = New-Object System.Collections.Generic.List[System.Object] # Third method

for ($d = $startDate; $d -le $endDate; $d = $d.AddDays(1)) {
if ($d.DayOfWeek -ne 'Sunday') {
$datesArray += $d # First and second method: += is the method to add elements to: Powershell array; or .NET ArrayList (confusing)
#$datesArray.Add($d) # Third method: .Add is the method to add elements to: .NET Generic List
}
}

Return $datesArray
}

# Get one week of dates, ending with yesterday's date
$startDate = Get-Date
$endDate = $startDate.AddDays(-1) # Get yesterday's date as last date in range
$startDate = $endDate.AddDays(-7) # Get 7th prior date as first date in range

$datesList = Get-DateRangeList $startDate $endDate

# Loop through the dates
Foreach ($d in $datesList) {
# Do something with each date, e.g., format the date as part of a list
# of date-stamped files to retrieve
"FileName_{0}.txt" -f $d.ToString("yyyyMMdd")
}

其次,使用 .NET Framework ArrayList :
Function Get-DateRangeList {
[cmdletbinding()]
Param (
[datetime] $startDate,
[datetime] $endDate
)

#$datesArray = @() # First method
$datesArray = [System.Collections.ArrayList]@() # Second method
#$datesArray = New-Object System.Collections.Generic.List[System.Object] # Third method

for ($d = $startDate; $d -le $endDate; $d = $d.AddDays(1)) {
if ($d.DayOfWeek -ne 'Sunday') {
$datesArray += $d # First and second method: += is the method to add elements to: Powershell array; or .NET ArrayList (confusing)
#$datesArray.Add($d) # Third method: .Add is the method to add elements to: .NET Generic List
}
}

Return $datesArray
}

# Get one week of dates, ending with yesterday's date
$startDate = Get-Date
$endDate = $startDate.AddDays(-1) # Get yesterday's date as last date in range
$startDate = $endDate.AddDays(-7) # Get 7th prior date as first date in range

$datesList = Get-DateRangeList $startDate $endDate

# Loop through the dates
Foreach ($d in $datesList) {
# Do something with each date, e.g., format the date as part of a list
# of date-stamped files to retrieve
"FileName_{0}.txt" -f $d.ToString("yyyyMMdd")
}

第三,使用 .NET Framework Generic List:
Function Get-DateRangeList {
[cmdletbinding()]
Param (
[datetime] $startDate,
[datetime] $endDate
)

#$datesArray = @() # First method
#$datesArray = [System.Collections.ArrayList]@() # Second method
$datesArray = New-Object System.Collections.Generic.List[System.Object] # Third method

for ($d = $startDate; $d -le $endDate; $d = $d.AddDays(1)) {
if ($d.DayOfWeek -ne 'Sunday') {
#$datesArray += $d # First and second method: += is the method to add elements to: Powershell array; or .NET ArrayList (confusing)
$datesArray.Add($d) # Third method: .Add is the method to add elements to: .NET Generic List
}
}

Return $datesArray
}

# Get one week of dates, ending with yesterday's date
$startDate = Get-Date
$endDate = $startDate.AddDays(-1) # Get yesterday's date as last date in range
$startDate = $endDate.AddDays(-7) # Get 7th prior date as first date in range

$datesList = Get-DateRangeList $startDate $endDate

# Loop through the dates
Foreach ($d in $datesList) {
# Do something with each date, e.g., format the date as part of a list
# of date-stamped files to retrieve
"FileName_{0}.txt" -f $d.ToString("yyyyMMdd")
}

所有这三个工作。为什么您偏爱一个?本机PowerShell数组和.NET Framework的 ArrayList类都生成非强类型的对象的集合,因此您可以这样做(在Powershell数组实现中):
$myArray = @(1, 2, 3, "A", "B", "C")

Powershell阵列对于非常大的阵列而言效率不高。对于非常大的集合,ArrayList是更好的选择。

对于大量相同类型的对象,似乎.NET Framework通用列表是最佳选择。在我的示例中,我想要一个日期列表。每个日期都是相同的数据类型,因此无需混合对象类型。因此,我正在部署的解决方案是上面的第三个工作示例。

感谢Dave Wyatt在2013年Powershell.org上发表的有关 PowerShell Performance: The += Operator (and When to Avoid It)的文章。特别是 +=方法 creates a new array object in each pass within a loop,添加新元素,然后销毁旧数组。对于大量收集,这将变得非常低效。

我正在发布这些解决方案和讨论,以希望其他初学者可以更轻松地找到我所寻求的答案。

是的-是的-我不遵循某些人的想法,即严格的PowerShell语法礼节。我在函数中使用了 return语句,因此很明显该函数产生了什么。我更喜欢可读性强的代码,这些代码看起来可能比扩展的要紧。那是我的偏爱,我坚持下去。

有关日期列表的更像PowerShell的实现,我请读者引用 tidy implementation posted by The Surly Admin

最佳答案

关于OP的第3段:Collections.arraylist确实可以在Powershell中工作,例如:

# Create arraylist with space for 20 object
$ar = new-object collections.arraylist 20
$ar.add("hello everybody")
$ar.add([datetime]::now)
$ar.add( (gps)[9])
$ar[0] # returns string
$ar[1] # returns datetime
$ar[2] # returns tenth process
$ar.count # returns 3

我认为这是要更仔细地阅读arraylist的MSDN文档。

如果在PS中的arraylist上使用+ =,它将从arraylist中获取元素,并获取新元素并创建一个数组。我相信,这是一种使用户免受偶然发现的.NET复杂性影响的尝试。 (我怀疑PS产品团队的主要用例之一是对.NET尤其是arraylist不太熟悉的用户。您显然不属于该类别。)

我将提到PS和阵列的绊脚石。 PS在某些情况下会自动展开阵列。例如,如果我有一个字符数组,并且想要创建一个字符串(使用String..ctor([char []])重载),则此方法将无效:
# Fails because PS unrolls the array and thinks that each element is a
# different argument to String..ctor
$stringFromCharArray = new-object string $charArray
# Wrap $charArray to get it to work
$stringFromCharArray = new-object string @(,$charArray)
# This also works
$stringFromCharArray = new-object string (,$charArray)

当您沿管道向下传递数组时,也存在类似的问题。如果要使数组沿管道传递(相对于数组元素),则需要先将其包装在另一个数组中。

关于arrays - Powershell阵列:何时使用它们;什么时候避免他们和使用它们的问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34109398/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com