Назад | Перейти на главную страницу

Как передать как ArrayList, предустановленные данные в блоке параметров и вернуть как ArrayList?

Поскольку мне нужно иметь возможность добавлять и удалять элементы из массива, мне нужно преобразовать его как ArrayList в отличие от более распространенных вариантов ([string[]], [Array], $Var=@()). Мне также нужно предварительно установить его с данными, но вызывающему функции нужна возможность изменить предустановку по своему желанию. Предустановка в Param() блок требуется, поскольку другой объект ищет предустановленные данные. Я пробовал несколько вариантов, но вот тот, который имеет наибольший смысл:

Function Test-Me{
    Param
    (
     $Properties = [System.Collection.ArrayList]@(
                   "red",
                   "blue",
                   "green")
    )
    $Properties.GetType()
    $Properties.Add("orange")
}

Вышеизложенное прекрасно, за исключением того, что как только кто-то позвонит Test-Me -Properties "Purple","Yellow","Black" то $Properties переменная становится стандартом Array type (при этом методы добавления и удаления не работают).

Я пробовал методы изменения того, как я объявлял предустановленные значения. Похоже, что предварительное заполнение преобразует тип в обычный массив. Я подумал, что это потому, что я использовал @() с предустановками, поэтому я пробовал () также.

Это тоже не работает:

Param
(
[System.Collection.ArrayList]
 $Properties = @("red",
                 "blue",
                 "green")
)

У меня есть работа, которая преобразует тип вне блока Param, и это выглядит так:

Function Test-Me{
    Param
    (
     [String[]]
     $Properties = @("red",
                     "blue",
                     "green")
    )

    if("Orange" -notin $Properties){
        [System.Collections.ArrayList]$Properties = $Properties
        $Properties.Add("orange")
    }
}

Я чувствую, что могу сыграть роль ArrayList в блоке параметров и предустановить его данными, и вернуться с тем же типом данных, но я не мог этого понять. Если кто-то это сделает или найдет документацию, почему это не сработает, ответьте.

Явное приведение типа параметра

Второй опубликованный вами пример (явное приведение переменной параметра) является правильный путь:

Function Test-Me {
    Param(
        [System.Collections.ArrayList]
        $Properties = @("red","blue","green")
    )

    Write-Host $Properties.GetType().FullName

    if("Orange" -notin $Properties){
        [void]$Properties.Add("orange")
    }

    $Properties
}

В результате чего:

PS C:\> Test-Me
System.Collections.ArrayList
red
blue
green
orange

PS C:\> Test-Me -Properties "one","two","three"
System.Collections.ArrayList
one
two
three
orange

OutputType

Одна вещь, которая меня удивила, это то, что даже с [OutputType] атрибут, функция выводит обычный массив (на самом деле это может быть Жук предполагаемое поведение, см. обновление ниже):

Function Test-Me {
    [OutputType([System.Collections.ArrayList])]
    Param(
        [System.Collections.ArrayList]
        $Properties = @("red","blue","green")
    )
    
    if("Orange" -notin $Properties){
        [void]$Properties.Add("orange")
    }

    $Properties
}

По-прежнему возвращается обычный массив объектов:

PS C:\> (Test-Me).GetType()

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

Обновление (с простым обходным путем)

Как показано в комментарии к вашему Подключить сообщение об ошибкеPowerShell намеренно перечисляет элементы любого перечисляемого вывода, чтобы обеспечить согласованное поведение конвейера (псевдо-C #, украденный у комментатора Дерп МакДерп на Connect):

if (returned_value is IEnumerable) {
    foreach (r in returned_value) {
        yield r;
    }
} else {
    yield returned_value;
}

Уловка состоит в том, чтобы обернуть коллекцию в массив с одним элементом, заставляя PowerShell передавать ее по конвейеру как отдельный элемент (примечание к , перед $Properties):

Function Test-Me {
    [OutputType([System.Collections.ArrayList])]
    Param(
        [System.Collections.ArrayList]
        $Properties = @("red","blue","green")
    )
    
    if("Orange" -notin $Properties){
        [void]$Properties.Add("orange")
    }

    ,$Properties
}

и теперь мы получаем результат правильного типа:

PS C:\> (Test-Me).GetType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     ArrayList                                System.Object

Сотрудник просто просил меня объяснить некоторые детали, связанные с этим, поэтому я подумал, что может быть полезно поделиться некоторой дополнительной информацией с другими, кто придет сюда с этим вопросом в будущем.

В PowerShell есть три основных способа присвоения значений переменным:

# Loosely typed
# Can be reassigned to anything without error
$myVariable1 = @('Red','Green','Blue')

# Type cast
# Still loosely typed
# Can still be reassigned to anything without issue
$myVariable2 = [System.Collections.ArrayList]@('Red','Green','Blue')

# Strongly typed
# Can only be assigned objects of that type or objects that implicitly convert into that type
[System.Collections.ArrayList]$myVariable3 = 'Red','Green','Blue'

Если вы хотите убедиться, что переменная (или параметр функции) имеет определенный тип, вы должны использовать строгую типизацию, как это было сделано с $myVariable3. Это предотвратит случайное изменение типа на что-то другое (например, $myVariable3 = 42 выйдет из строя с ошибкой).

Полезно знать, что вы можете определить, когда переменная строго типизирована, используя Get-Variable командлет. Например, вызов Get-Variable myVariable1,myVariable2,myVariable3 | Format-List Name,Value,Visibility,Options,Attributes дает следующий результат:

Name       : myVariable1
Value      : {Red, Green, Blue}
Visibility : Public
Options    : None
Attributes : {}

Name       : myVariable2
Value      : {Red, Green, Blue}
Visibility : Public
Options    : None
Attributes : {}

Name       : myVariable3
Value      : {Red, Green, Blue}
Visibility : Public
Options    : None
Attributes : {System.Management.Automation.ArgumentTypeConverterAttribute}

Обратите внимание, что все они одинаковы, за исключением того, что myVariable3 переменная имеет атрибут в Attributes свойство. An ArgumentTypeConverterAttribute добавляется к любой строго типизированной переменной, и этот атрибут гарантирует, что переменная останется одного и того же типа независимо от того, чему вы ее назначите.

Понимание того, как работает присваивание переменных, а также как вернуть коллекцию без изменений, как указал Матиас выше, значительно упрощает работу с данными в PowerShell.