PowerShell函数参数-按引用还是按值? [英] PowerShell Function parameters - by reference or by value?

查看:82
本文介绍了PowerShell函数参数-按引用还是按值?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

因此,我尝试查找此问题的答案,发现通常可用的答案是PowerShell按值传递参数.这些普遍接受的解决方案均张贴示例代码以证明其断言,类似于以下内容:

So, I tried looking up the answer to this question, and found the generally available answer is that PowerShell passes parameters by value. These generally accepted solutions all post sample code to prove their assertions, similar to the following:

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已经存在.在函数内部.

Thus proving that parameters are passed by value, right? Well, I was having a heck of a time troubleshooting a function I was writing. The function added a couple of additional NoteProperty items to a PSCustomObject I pass in to the function, and my program would throw all sorts of errors saying that the NoteProperty already existed, even though I had not modified the original object in the parent scope, only inside the function.

因此,我设置了上述代码的版本,以使用[PSCustomObject]类型的参数进行测试,如下所示:

So, I set up a version of the above code to test using parameter of type [PSCustomObject], like so:

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)"

这个凌乱的编程片段会产生以下输出:

This messy piece of programming produces the following output:

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自己的有关Function Parameters的文档,但没有看到此异常.

These results clearly show that when [PSCustomObject] parameters are used, any modifications within the function take place on the passed object, thus pass by reference. This behavior happens regardless of defining my parameters as [PSCustomObject]$Obj, or leaving them untyped. This is not a huge problem in and of itself, but the problem is that I was unable to find this little gem of information in any of the documentation I looked through. I checked a few tutorial sites and Microsoft's own documentation on Function Parameters, but did not see this exception.

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

So, my question boils down to this: Has anyone found any documentation to support my theory that while most parameters default to passing by value, they are passed by reference when objects are concerned?

我非常愿意相信我在某个地方错过了一些文档,所以请...指出来,向我展示我的方式中的错误! :)

I am perfectly willing to believe that I missed some documentation somewhere, so please...point it out and show me the error of my ways! :)

非常感谢

推荐答案

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

Note: The following also applies to assigning one variable to another: $b = $a ...
* makes $b reference the very same object that $a does if $a's value is an instance of a reference type,
* makes $b receive an independent copy of $a's value if the latter is an instance of a value type.

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

  • PowerShell uses by-(variable)-value passing by default; that is, the content of a variable is passed, not a reference to the variable itself.

    如果要通过-(变量)-引用,即需要将变量本身传递给 ,允许被调用方使用
  • 额外的精力.都获取变量的内容并分配新内容;以最简单的形式,您可以使用[ref]类型的参数(类似于C#中的ref参数).但是,请注意,在PowerShell中很少需要使用此技术.
  • Extra effort is needed if you want by-(variable)-reference passing, i.e. if you want to pass a reference to a variable itself, allowing the callee to both get the variable's content and to assign new content; in the simplest form, you can use a [ref]-typed parameter (akin to ref parameters in C#). However, note that this technique is rarely necessary in PowerShell.

内容是呼叫者看到的内容的副本还是对同一对象的引用取决于数据类型 :

Whether that content is a copy of what the caller sees or a reference to the very same object depends on the data type of the content:

  • 如果内容碰巧是 .NET

  • If the content happens to be an instance of a .NET reference type - as [pscustomobject] is - that content is an object reference, and the callee can therefore potentially modify that object, by virtue of seeing the very same object as the caller.

  • 如果要传递引用类型实例的副本(克隆),请注意,没有 no 通用机制可以创建一:
    • 您可以创建类型为 if 的实例的副本,如果它们实现 System.ICloneable 接口,方法是调用其.Clone()方法,但是请注意,执行 shallow 还是 deep 取决于实现类型em> cloning [1] ;出于这个原因,不鼓励使用此接口;实际上,实现它的类型通常执行 shallow 克隆,特别是数组,数组列表(System.Collections.ArrayList)和哈希表(但请注意,[ordered]哈希表(System.Collections.Specialized.OrderedDictionary)并没有实现)完全ICloneable.
    • 此外,在PowerShell中,您可以在[pscustomobject]类型的实例上调用.psobject.Copy()来创建 shallow 副本. (不要在任何其他类型的对象上使用此方法,因为它们实际上将是无操作的.)类似地,单个.NET类型可以实现自定义克隆方法.
    • If you want to pass a copy (clone) of a reference-type instance, note that there is no universal mechanism for creating one:
      • You can create copies of instances of types if they implement the System.ICloneable interface by calling their .Clone() method, but note that it is up to the implementing type whether to perform shallow or deep cloning[1]; it is for that reason that use of this interface is discouraged; in practice, types that do implement it typically perform shallow cloning, notably arrays, array lists (System.Collections.ArrayList) and hashtables (but note that an [ordered] hashtable (System.Collections.Specialized.OrderedDictionary) doesn't implement ICloneable at all.
      • Additionally, in PowerShell, you can call .psobject.Copy() on instances of type [pscustomobject] to create a shallow copy. (Do not use this method on objects of any other type, where it will effectively be a no-op.) Similarly, individual .NET types may implement custom cloning methods.

      相比之下,如果该内容是 .NET 值类型 -例如,[int]- string [2] ,该实例的独立副本 被传递.

      If, by contrast, that content is an instance of a .NET value type - e.g., [int] - or a string[2], an independent copy of that instance is passed.

      这种区别是.NET的基础,并非特定于PowerShell;例如,这也是在C#中传递参数的方式.

      This distinction is fundamental to .NET, not specific to PowerShell; it is also how arguments are passed in C#, for instance.

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

      To determine whether a given variable's value is an instance of a value type or a reference type, use something like the following:

      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.

      If you look up the documentation for a given .NET type, say System.DateTime, the inheritance information will start with Object -> ValueType for value types; in C# terms, a value type is either a struct or an enum, whereas a reference type is a class.

      这里有两个不相关的概念,它们都使用了(by-)value (by-)reference 可能会引起混淆:

      There are two unrelated concepts at play here, and the fact that they both use the terms (by-)value and (by-)reference can get confusing:

      • 按(可变)值与按(可变)参考参数传递数据持有人(可变)概念:

      • By-(variable)-value vs. by-(variable)-reference parameter-passing is a data-holder (variable) concept:

      • 它描述了在传递参数时是按值传递变量的 value 还是对变量本身的引用 [3] (通过引用).
      • It describes whether, on parameter passing, a variable's value is passed (by value) or a reference to the variable itself[3] (by reference).

      引用类型与值类型纯粹是一个 数据概念:

      Reference types vs. value types is purely a data concept:

      • 也就是说,出于技术原因, .NET中的任何对象都是值类型的实例(存储在 stack 中)或引用类型的实例(存储在中)堆).前者的实例直接 存储在变量中,而后者的实例通过 reference 存储.因此,复制一个变量 value -例如,在按值传递参数的情况下-表示:
        • 两者之一:为其自身复制值类型实例的一个副本,从而生成一个独立的数据副本.
        • or:复制引用类型实例 reference ;但是,引用的副本仍指向同一对象,这就是为什么被调用者甚至直接看到通过变量值传递的引用类型实例的原因.参考副本的方式.
        • That is, for technical reasons, any object in .NET is either an instance of a value type (stored on the stack) or a reference type (stored on the heap). Instances of the former are directly stored in variables, whereas the latter are stored by way of a reference. Therefore, copying a variable value - e.g., in the context of by-value parameter-passing - means:
          • either: making a copy of a value-type instance itself, resulting in an independent data copy.
          • or: making a copy of a reference-type instance reference; a copy of a reference still points to the same object, however, which is why even by-variable-value passed reference-type instances are directly seen by the callee (by way of their reference copy).

          [1] 克隆意味着将引用类型实例的属性值按原样复制-作为引用-这意味着克隆的属性值再次引用同一对象作为原始的. Deep 克隆是指以递归方式克隆此类属性值.深度克隆很昂贵,而且并非总是可能.

          [1] Shallow cloning means that property values that are reference-type instances are copied as-is - as references - which means that the clone's property value again references the very same object as the original. Deep cloning means that such property values are cloned themselves, recursively. Deep cloning is expensive and isn't always possible.

          [2]字符串([string])在技术上也是 ,也是 reference 类型的实例,但作为例外,它的处理方式类似于值类型;有关此异常背后的原因,请参见此答案.

          [2] A string ([string]) is technically also an instance of a reference type, but, as an exception, it is treated like a value type; see this answer for the rationale behind this exception.

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

          [3] Another way of thinking about it: a reference (pointer) to the location where the variable stores its value is passed. This allows the callee to not only access the variable's value, but also to assign a (new) value.

          这篇关于PowerShell函数参数-按引用还是按值?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆