function - PowerShell 函数参数 - 按引用还是按值?

标签 function powershell parameters

所以,我尝试查找这个问题的答案,发现普遍可用的答案是 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 还是不输入它们,都会发生这种行为。这本身并不是一个大问题,但问题是我无法在我浏览的任何文档中找到这个小信息。我检查了一些教程站点和 Microsoft 自己关于函数参数的文档,但没有看到这个异常。

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

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

非常感谢

最佳答案

注意:以下 也适用于 将一个变量分配给另一个 : $b = $a ...
* 如果 $b 的值是引用类型的实例,则使 $a 引用与 $a 相同的对象,
* 如果后者是值类型的实例,则使 $b 接收 $a 值的独立副本。

  • PowerShell 默认使用 by-(variable)-value 传递 ;也就是说, 传递的是变量的内容 ,而不是对变量本身的引用。
  • 如果你想通过-(变量)-引用传递,则需要额外的努力,即如果你想传递一个对变量本身的引用,允许被调用者获取变量的内容并分配新的内容;在最简单的形式中,您可以使用 [ref] 类型的参数(类似于 C# 中的 ref 参数)。但是,请注意,在 PowerShell 中很少需要此技术。
  • 该内容是调用者所见内容的副本还是对同一对象的引用取决于内容 的数据类型:
  • 如果内容恰好是一个 .NET reference type 的一个实例 - 作为[pscustomobject]是 - 即内容是一个对象引用,和被叫方因此可以潜在地修改该对象,凭借看到非常相同的对象的作为来电者。
  • 如果要传递引用类型实例 的副本(克隆),请注意创建一个没有通用机制:
  • 如果类型的实例实现了 System.ICloneable 接口(interface),则可以通过调用其 .Clone() 方法创建类型实例的副本,但请注意,执行浅克隆还是深克隆取决于实现类型[1];正是出于这个原因,不鼓励使用此接口(interface);在实践中,实现它的类型通常执行浅克隆,特别是数组、数组列表( System.Collections.ArrayList )和哈希表(但请注意, [ordered] 哈希表( System.Collections.Specialized.OrderedDictionary )根本没有实现 ICloneable 60714。
  • 此外,在 PowerShell 中,您可以在 .psobject.Copy() 类型的实例上调用 [pscustomobject] 以创建浅拷贝。 (不要在任何其他类型的对象上使用此方法,它实际上是一个无操作。)同样,单个 .NET 类型可以实现自定义克隆方法。
  • 相比之下,如果该内容是 .NET value type 的实例 - 例如,[int] -
    string[2]
    ,该实例的 独立副本 被传递。
  • 这种区别是 .NET 的基础 ,不特定于 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# 术语中,值类型是 structenum ,而引用类型是 class

    术语和概念:

    这里有 两个不相关的概念 在这里起作用,而且它们都使用术语 (by-)value 和 (by-)reference 的事实可能会令人困惑:
  • By-(variable)-value vs. by-(variable)-reference parameter-passing 数据持有者(变量)概念 :
  • 它描述了在参数传递时,是传递变量的值(按值)还是对变量本身的引用[3](按引用)。
  • 引用类型与值类型 纯粹是一个 数据概念 :
  • 也就是说,出于技术原因, .NET 中的任何对象要么是值类型的实例(存储在堆栈上)要么是引用类型(存储在堆上)。前者的实例直接存储在变量中,而后者通过引用 存储。因此,复制变量值 - 例如,在按值参数传递的上下文中 - 意味着:
  • 要么:制作值类型实例本身的副本,从而产生独立的数据副本。
  • 或:制作引用类型实例引用的副本;然而,引用的副本仍然指向同一个对象,这就是为什么即使是按变量值传递的引用类型实例也能被被调用者直接看到(通过它们的引用副本)。


  • [1] 浅克隆意味着作为引用类型实例的属性值按原样复制 - 作为引用 - 这意味着克隆的属性值再次引用与原始对象完全相同的对象。深度克隆意味着这些属性值被递归地克隆。深度克隆成本高昂,而且并非总是可行。

    [2] 字符串 ( [string] ) 在技术上也是引用类型的实例,但作为一个异常(exception),它被视为值类型;有关此异常背后的基本原理,请参阅 this answer

    [3] 另一种思考方式:传递指向变量存储其值的位置的引用(指针)。这允许被调用者不仅可以访问变量的值,还可以分配(新)值。

    关于function - PowerShell 函数参数 - 按引用还是按值?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60102320/

    相关文章:

    sql-server - MS SQL 查询这安全吗?

    python - 如何通过Python文件在控制台运行函数?

    javascript - 当我在函数参数 : Why is that? 中声明新变量时出现错误

    javascript - 处理必需和可选函数参数的推荐方法

    c++ - 尝试构建基于模板的代码时出错

    sql - SQL分组依据用户定义函数的输出

    windows - 如何查找计算机在某一天的首次开机时间和最后一次关机时间?

    sharepoint - 在SharePoint 2010中运行Move-SPSite移动网站集时出错

    powershell - 使用名为length的键的Powershell哈希表

    parameters - xgboost : The meaning of the base_score parameter