【发布时间】:2015-12-05 18:41:41
【问题描述】:
为什么 .NET Framework ArrayList 类 .Add 方法在 PowerShell 实现中不起作用?
除非我另有纠正,否则我认为我的故事的整体寓意可能是:不要假设本机 PowerShell 方法将与 .NET 方法相同,并且在尝试 PowerShell 中的 .NET 方法时要小心.
我寻求的原始解决方案是从一个函数中返回一个日期列表,作为一个数组,并以用户定义的日期范围作为参数。然后将引用日期数组来移动和读取以日期标记命名的文件。
我遇到的第一个问题是创建一个动态数组。我不知道自己在做什么,并且在 @() 数组声明上错误地调用了 .NET .Add 方法。
使用“1”个参数调用“添加”的异常:“集合的大小是固定的。”
我认为我需要找到一个动态数组类型,而我真正的问题是我做得不对。这让我朝着不同的方向前进,直到很久以后,我才发现应该使用 += 语法将对象添加到 PowerShell 数组中。
不管怎样,在我回到如何正确使用 PowerShell 数组之前,我已经离开了其他一些切线。
然后我找到了 .NET ArrayList 类。好的。现在我有一个动态数组对象。我阅读了文档,其中说我应该使用.Add 方法将元素添加到集合中。
然后我开始寻求更深入的理解,因为我在解决问题时经历了几天的头晕目眩的挫败感。
我制作了一个起初似乎可行的实现。它产生了一个日期范围——但它也产生了一些奇怪的行为。我观察到返回的日期很奇怪,例如:
0001 年 1 月 1 日星期一 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 Generic List 似乎是对于相同类型的非常大的对象集合的最佳选择。在我的示例中,我想要一个日期列表。每个日期都是相同的数据类型,所以我不需要混合对象类型。因此,我正在部署的解决方案是上面的第三个工作示例。
我很欣赏 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。
【问题讨论】:
-
拥抱 PowerShell 的管道,它让您尝试做的事情变得更加容易。
-
ArrayList.Add返回添加元素的索引,并且即使没有return语句,PowerShell 也会返回任何内容,它会返回该索引,您已经以某种方式消除了它:[void]$datesArray.Add($d)。+=不会向ArrayList添加元素:$a=New-Object Collections.ArrayList;$a+=1;$a.GetType(),因此您的第二个示例不适用于ArrayList,但适用于数组,与第一个相同。而且,恕我直言,不要使用@(1, 2, 3, "A", "B", "C"):(1, 2, 3, "A", "B", "C")产生相同的结果,少输入一个字符,并且不会进行不必要的数组复制。
标签: arrays powershell arraylist generic-list