Параметры функции PowerShell - по ссылке или по значению? - PullRequest
1 голос
/ 06 февраля 2020

Итак, я попытался найти ответ на этот вопрос и обнаружил, что общедоступным ответом является то, что PowerShell передает параметры по значению. Эти общепринятые решения все после кода образца, чтобы доказать свои утверждения, аналогично следующему:

Function add1 ($parameter)
{
    Write-Host "    In Function: `$parameter = $parameter"
    Write-Host "    In Function: `$parameter += 1"
    $parameter += 1
    Write-Host "    In Function: `$parameter = $parameter"
}

cls
$a = 1
Write-Host "Before function: `$a = $a"
add1 $a
Write-Host " After function: `$a = $a"

Это дает результаты:

Before function: Run Command: $a = 1
    In Function: $parameter: 1
    In Function: Run Command: $parameter += 1
    In Function: $parameter: 2
 After function: $a: 1

Таким образом, доказывая, что параметры передаются по значению, правильно? Ну, у меня было чертовски много времени на устранение неполадок с функцией, которую я писал. Функция добавила несколько дополнительных элементов NoteProperty к объекту PSCustomObject, который я передал функции, и моя программа выдала бы все виды ошибок, говоря, что NoteProperty уже существует, даже если я не изменял исходный объект в родительской области, только внутри функции.

Итак, я настроил версию вышеприведенного кода для проверки с использованием параметра типа [PSCustomObject], например так:

Function F1($Obj)
{
    'Function F1: Run command: $Obj.FirstValue = 11'
    $Obj.FirstValue = 11
    "             `$Obj.Name: $($StartObject.Name)"
    "             `$Obj.FirstValue: $($StartObject.FirstValue)"
    "             `$Obj.SecondValue: $($StartObject.SecondValue)"
}

Function F2($Obj)
{
    'Function F2: Run command: $Obj | Add-Member -MemberType NoteProperty -Name SecondValue -Value 33'
    $obj | Add-Member -MemberType NoteProperty -Name SecondValue -Value 33
    "             `$Obj.Name: $($StartObject.Name)"
    "             `$Obj.FirstValue: $($StartObject.FirstValue)"
    "             `$Obj.SecondValue: $($StartObject.SecondValue)"
}

cls
Remove-Variable StartObject
"Main script: Run command: `$StartObject = [PSCustomObject]@{Name='Original';FirstValue=22}"
$StartObject = [PSCustomObject]@{Name='Original';FirstValue=22}
"             `$StartObject.Name: $($StartObject.Name)"
"             `$StartObject.FirstValue: $($StartObject.FirstValue)"
"             `$StartObject.SecondValue: $($StartObject.SecondValue)"
'Run command: F1 $StartObject'
" "
F1 $StartObject
" "
"Main script: `$StartObject.Name: $($StartObject.Name)"
"             `$StartObject.FirstValue: $($StartObject.FirstValue)"
"             `$StartObject.SecondValue: $($StartObject.SecondValue)"
"Run command: F2 $StartObject"
" "
F2 $StartObject
" "
"Main script: `$StartObject.Name = $($StartObject.Name)"
"             `$StartObject.FirstValue = $($StartObject.FirstValue)"
"             `$StartObject.SecondValue = $($StartObject.SecondValue)"

Этот грязный фрагмент программирования создает следующий вывод:

Main script: Run command: $StartObject = [PSCustomObject]@{Name='Original';FirstValue=22}
             $StartObject.Name: Original
             $StartObject.FirstValue: 22
             $StartObject.SecondValue: 
Run command: F1 $StartObject

Function F1: Run command: $Obj.FirstValue = 11
             $Obj.Name: Original
             $Obj.FirstValue: 11
             $Obj.SecondValue: 

Main script: $StartObject.Name: Original
             $StartObject.FirstValue: 11
             $StartObject.SecondValue: 
Run command: F2 @{Name=Original; FirstValue=11}

Function F2: Run command: $Obj | Add-Member -MemberType NoteProperty -Name SecondValue -Value 33
             $Obj.Name: Original
             $Obj.FirstValue: 11
             $Obj.SecondValue: 33

Main script: $StartObject.Name = Original
             $StartObject.FirstValue = 11
             $StartObject.SecondValue = 33

Эти результаты ясно показывают, что при использовании параметров [PSCustomObject] любые изменения в функции происходят в переданном объекте, таким образом, передаются по ссылке. Это происходит независимо от определения моих параметров как [PSCustomObject] $ Obj или без их изменения. Само по себе это не является большой проблемой, но проблема в том, что я не смог найти этот маленький драгоценный камень информации ни в одной из просмотренных мной документов. Я проверил несколько учебных сайтов и собственную документацию Microsoft по параметрам функций, но не увидел этого исключения.

Итак, мой вопрос сводится к следующему: кто-нибудь нашел какую-либо документацию, поддерживающую мою теорию, хотя большинство параметров по умолчанию чтобы передать по значению, они передаются по ссылке, когда речь идет об объектах?

Я совершенно готов поверить, что где-то пропустил какую-то документацию, поэтому, пожалуйста ... укажите это и покажите мне ошибку моих путей ! :)

Большое спасибо

1 Ответ

3 голосов
/ 06 февраля 2020
  • PowerShell использует by- (переменную) - значение по умолчанию ; то есть содержимое переменной передается , а не ссылка на саму переменную.

    • Дополнительные усилия необходимы, если вы хотите - ( переменная) - передача передача, т. е. если вы хотите передать ссылку на саму переменную , что позволяет вызываемому абоненту одновременно получать содержимое переменной и назначать новое содержимое ; в простейшей форме вы можете использовать [ref] -типированный параметр (похожий на ref параметры в C#). Однако обратите внимание, что этот метод редко необходим в PowerShell.
  • Является ли этот контент копией того, что видит вызывающий объект, или ссылка на тот же объект зависит от типа данных содержимого :

    • Если содержимое является экземпляром a . NET тип ссылки - поскольку [pscustomobject] означает, что содержимое является объектом ссылка и вызываемый абонент может потенциально изменить этот объект , поскольку он видит тот же самый объект , что и вызывающий объект.

      • Если вы хотите чтобы передать копию (клон) экземпляра ссылочного типа , обратите внимание, что существует нет универсальный механизм для его создания:
        • Вы можете создавать копии экземпляров типов , если они реализуют интерфейс System.ICloneable, вызывая их метод .Clone(), но учтите, что это зависит от Тип записи: выполнять неглубоко или глубоко клонировать [1] ; именно по этой причине использование этого интерфейса не рекомендуется; на практике типы, которые его реализуют, обычно выполняют клонирование поверхностное , особенно массивы, списки массивов (System.Collections.ArrayList) и хеш-таблицы (но учтите, что хеш-таблица [ordered] (System.Collections.Specialized.OrderedDictionary) не реализует *) 1082 * вообще.
        • Кроме того, в PowerShell вы можете вызывать .psobject.Copy() в экземплярах типа [pscustomobject], чтобы создать мелкую копию. (Делать не используйте этот метод для объектов любого другого типа, где он фактически будет недоступен.) Точно так же отдельные. NET типы могут реализовать собственные методы клонирования.
    • Если, напротив, этот контент является экземпляром . NET тип значения - например, , [int] - или строка [2] , независимая копия этого экземпляра передается .

    • Это различие является основополагающим для. NET, не указывается c для PowerShell, а также для передачи аргументов в C#, для экземпляр.

Чтобы определить, является ли значение данной переменной экземпляром типа значения или ссылочного типа, используйте что-то вроде следующего:

1, (Get-Date), (Get-Item /) |  # sample values
  foreach {
    '{0} is of type {1}; is it a value type? {2}' -f $_, 
                                                     $_.GetType(),
                                                     $_.GetType().IsValueType
  }

Вы увидите что-то вроде:

1 is of type System.Int32; is it a value type? True
4/30/2020 12:37:01 PM is of type System.DateTime; is it a value type? True
/ is of type System.IO.DirectoryInfo; is it a value type? False

Если вы посмотрите документацию для данного типа. NET, скажем System.DateTime, информация о наследовании будет начинаться с Object -> ValueType для типов значений; в терминах C# типом значения является struct или enum, а ссылочным типом является class.


Терминология и понятия:

Здесь присутствуют два не связанных между собой понятия , и тот факт, что они оба используют термины (by-) value и (by-) reference , может сбить с толку :

  • Передача параметров по значению (переменная) по отношению к переменной (переменная) является держателем data- (переменная) концепция :

    • Описывает, передается ли при передаче параметра значение переменной (по значению) или ссылка на Сама переменная [3] (по ссылке).
  • Ссылочные типы и типы значений - это просто данные концепция :

    • То есть по техническим причинам любой объект в. NET является либо экземпляром типа значения (хранится в стеке ), либо ссылочным типом (хранится в * 1189). * кучи ). Экземпляры первого непосредственно хранятся в переменных, тогда как последние хранятся в виде reference . Следовательно, копирование переменной значение - например, в контексте передачи параметра по значению - означает:
      • либо: создание копии типа значения сам экземпляр , что приводит к независимой копии данных .
      • или: создание копии экземпляра ссылочного типа reference ; однако копия ссылки по-прежнему указывает на того же объекта , поэтому даже передаваемые экземпляры ссылочного типа по значению переменной напрямую видны вызываемому объекту (посредством их эталонная копия).

[1] Мелкое клонирование означает, что значения свойств, которые являются ссылочными экземпляры -типа копируются как есть - как ссылки - это означает, что значение свойства клона снова ссылается на тот же объект, что и оригинал. Глубокое клонирование означает, что такие значения свойств клонируются сами по себе, рекурсивно. Глубокое клонирование стоит дорого и не всегда возможно.

[2] Строка ([string]) технически также является экземпляром ссылки тип, но, как исключение, он обрабатывается как тип значения; см. этот ответ для обоснования этого исключения.

[3] Другой способ думать об этом: ссылка (указатель) на место, где хранится переменная его значение передается. Это позволяет вызываемому пользователю не только получать доступ к значению переменной, но также назначать (новое) значение.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...