PowerShell Function parameters - by reference or by value?

后端 未结 1 1415
被撕碎了的回忆
被撕碎了的回忆 2020-12-22 03:28

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 solutio

相关标签:
1条回答
  • 2020-12-22 03:53

    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 uses by-(variable)-value passing by default; that is, the content of a variable is passed, not a reference to the variable itself.

      • 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:

      • 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.

        • 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.
      • 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.

      • 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
      }
    

    You'll see something like:

    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
    

    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.


    Terminology and concepts:

    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:

      • 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:

      • 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] 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] 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] 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.

    0 讨论(0)
提交回复
热议问题