Почему метод .Add класса .NET Framework ArrayList не работает в реализации PowerShell?
Если меня не исправят, я думаю, что общая мораль моей истории может быть такой: не думайте, что нативные методы PowerShell будут такими же, как методы .NET, и будьте осторожны при попытке использовать методы .NET в PowerShell.
Первоначальное решение, которое я искал, состояло в том, чтобы вернуть список дат из функции в виде массива с заданным пользователем диапазоном дат в качестве параметров. Затем массив дат будет использоваться для перемещения и чтения файлов, имена которых содержат отметки даты.
Первой проблемой, с которой я столкнулся, было создание динамического массива. Я не знал, что делаю, и неправильно вызывал метод .NET .Add
для объявления массива @()
.
Исключение, вызывающее «Добавить» с аргументом (-ами) «1»: «Коллекция имеет фиксированный размер».
Я думал, что мне нужно найти тип динамического массива, когда моя настоящая проблема заключалась в том, что я делал это неправильно. Это направило меня в другом направлении, пока гораздо позже я не обнаружил, что объекты должны добавляться в массивы PowerShell с использованием синтаксиса +=
.
В любом случае, я отвлекся от некоторых других аспектов, прежде чем вернулся к тому, как правильно использовать массив PowerShell.
Затем я нашел класс .NET ArrayList
. В порядке Хорошо. Теперь у меня был объект динамического массива. Я прочитал документацию, в которой говорилось, что я должен использовать метод .Add
для добавления элементов в коллекцию.
Затем начались мои поиски более глубокого понимания, когда я преодолела пару дней отчаянного разочарования, пытаясь решить проблемы.
Я создал реализацию, которая сначала казалась работающей. Он создал диапазон дат, но также привел к странному поведению. Я заметил странные возвращенные даты, такие как:
Понедельник, 1 января 0001 г., 00:00:00
Оказывается, я обнаружил, что это результат, полученный, когда вы делаете это:
Get-Date 0
ArrayList
возвращал сначала список значений индекса для элементов массива, а затем значения массива. Это не имело никакого смысла. Я начал выяснять, правильно ли я вызывал функции, испытывал ли я какую-то проблему с переменной областью видимости или просто сошел с ума.
Теперь я вполне убежден, что мое разочарование было вызвано отсутствием надежного справочника для начинающих, который не просто показывает пару примеров того, как реализовать простую реализацию массива, но и описывает некоторые предостережения с альтернативными решениями.
Позвольте мне объяснить здесь три способа реализации массивов/коллекций с решением для того, что я пытался создать, а именно со списком дат в диапазоне дат.
По какой-то причине я сначала подумал, что правильный метод добавления элемента в .NET ArrayList в Powershell — использовать метод .Add. Это задокументировано. Я до сих пор не понимаю, почему это не работает (серьезно - кто-нибудь, пожалуйста, просветите меня). Экспериментируя, я обнаружил, что могу получить точные результаты, используя метод +=
для добавления объектов в 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 а>:
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, по-видимому, является лучшим выбором для очень больших коллекций объектов одного типа. В моем примере мне нужен список дат. Каждая дата имеет один и тот же тип данных, поэтому мне не нужно смешивать типы объектов. Поэтому решение, которое я развертываю, является третьим рабочим примером выше.
Я ценю статью Дейва Вятта на Powershell.org 2013 года по теме: it/" rel="nofollow noreferrer">Производительность PowerShell: оператор += (и когда его следует избегать). В частности, метод +=
создает новый объект массива при каждом проходе. внутри цикла, добавляя новый элемент, а затем уничтожая старый массив. Это становится очень неэффективным с большой коллекцией.
Я публикую эти решения и обсуждение в надежде, что какой-нибудь другой новичок с большей готовностью найдет ответы, которые я искал.
Да, верно, я не придерживаюсь того, что некоторым людям кажется строгим синтаксическим этикетом PowerShell. Я использую оператор return
в функции, поэтому очевидно, что производит функция. Я предпочитаю читаемый код, который может выглядеть растянутым, а не тесным. Это мое предпочтение, и я придерживаюсь его.
Для более похожей на PowerShell реализации списка дат я отсылаю читателей к аккуратная реализация, опубликованная The Surly Admin.
ArrayList.Add
возвращает индекс добавленного элемента, а поскольку PowerShell возвращает что-либо даже без оператораreturn
, он возвращает этот индекс, вы каким-то образом устраните это:[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")
дает тот же результат, берет на один символ меньше для ввода и не делает ненужной копии массива. - person user4003407   schedule 06.12.2015