Function PowerShell功能参数-按引用还是按值?

Function PowerShell功能参数-按引用还是按值?,function,powershell,parameters,Function,Powershell,Parameters,因此,我尝试查找这个问题的答案,发现普遍可用的答案是PowerShell按值传递参数。这些普遍接受的解决方案都会发布示例代码来证明其断言,类似于以下内容: Function add1 ($parameter) { Write-Host " In Function: `$parameter = $parameter" Write-Host " In Function: `$parameter += 1" $parameter += 1 Write-Hos

因此,我尝试查找这个问题的答案,发现普遍可用的答案是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
从而证明参数是通过值传递的,对吗?嗯,我在写一个函数时遇到了麻烦。函数向我传递给函数的PSCustomObject添加了几个额外的NoteProperty项,我的程序会抛出各种错误,说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还是不进行类型化,都会发生这种行为。这本身并不是一个大问题,但问题是我无法在我查看的任何文档中找到这一小部分信息。我查看了一些教程网站和微软自己关于函数参数的文档,但没有看到这个异常

所以,我的问题归结为:有没有人找到任何文档来支持我的理论,即虽然大多数参数默认按值传递,但当涉及对象时,它们是按引用传递的

我完全愿意相信我在某个地方遗漏了一些文档,所以请……指出它并告诉我我的错误方法!:)

非常感谢

注意:以下也适用于将一个变量分配给另一个变量
$b=$a

*如果
$a
的值是引用类型的实例,
*使
$b
接收
$a
值的独立副本(如果后者是值类型的实例)

  • 默认情况下,PowerShell使用按-(变量)-值传递;也就是说,传递变量的内容,而不是变量本身的引用

    • 如果您希望通过-(变量)-引用传递,也就是说,如果您希望传递对变量本身的引用,从而允许被调用方获取变量的内容并分配新内容,则需要付出额外的努力;在最简单的形式中,可以使用
      [ref]
      类型的参数(类似于C#中的
      ref
      参数)。但是,请注意,在PowerShell中很少需要这种技术
  • 该内容是调用者看到的内容的副本还是对同一对象的引用取决于内容的数据类型:

    • 如果内容恰好是.NET的一个实例
      [pscustomobject]
      是-内容是一个对象引用因此被调用方可以通过看到与调用方完全相同的对象来修改该对象

      • 如果要传递引用类型实例的副本(克隆),请注意,创建引用类型实例没有通用机制:
        • 如果类型的实例通过调用其
          .Clone()
          方法实现接口,则可以创建其实例的副本,但请注意,执行浅克隆还是深克隆取决于实现类型[1];因此,不鼓励使用该接口;实际上,实现它的类型通常执行浅层克隆,特别是数组、数组列表(
          System.Collections.ArrayList
          )和哈希表(但请注意,
          [ordered]
          哈希表(
          System.Collections.Specialized.OrderedDictionary
          )根本不实现
          iclonable
        • 此外,在PowerShell中,您可以对类型为
          [pscustomobject]
          的实例调用
          .psobject.Copy()
          ,以创建浅拷贝。(不要在任何其他类型的对象上使用此方法,因为它实际上是禁止操作的。)类似地,单个.NET类型可以实现自定义克隆方法
    • 相反,如果该内容是.NET的实例,例如
      [int]
      -或 字符串[2],传递该实例的独立副本

    • 这种区别对于.NET来说是基本的,而不是特定于PowerShell;例如,它也是在C#中传递参数的方式

要确定给定变量的值是值类型的实例还是引用类型的实例,请使用以下内容:

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"
1,(获取日期),(获取项目/)|#样本值
弗雷奇{
{0}是{1}类型;它是值类型吗?{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类型的文档,例如,对于值类型,继承信息将以
Object->ValueType
开头;在C语言中,值类型是
struct
enum
,而引用类型是
class


术语和概念: 这里有两个不相关的概念
,它们都使用(按-值)和(按-引用)这两个术语,这一事实可能会让人困惑:

  • 通过-(变量)-值与通过-(变量)-参考参数传递是一种数据保持