I've seen this, but neither answer actually answers the question using named argument in the call to the function
This is for Powershell 7. No workflow. And really Named Parameters this time! Also not covered in the standard documentation here .. Starting to think that this isn't possible.
this works, but it is requires brackets with positional arguments, not named arguments
Function MyFunction ([ref][string]$MyRefParam) {
$MyRefParam.Value = "newValue";
}
$myLocal = "oldValue"
Write-Host $myLocal # Outputs: oldValue
MyFunction ([ref]$myLocal);
Write-Host $myLocal # Outputs: newValue
Of course we all know that the best way to call a Powershell function is with named argument MyFunction -Arg1 23
rather than positional arguments MyFunction 23
or MyFunction(23)
. These would not get through our Pull Requests!
But this doesn't work
Function MyFunction ([ref][string]$MyRefParam) {
$MyRefParam.Value = "newValue";
}
$myLocal = "oldValue"
Write-Host $myLocal # Outputs: oldValue
MyFunction -MyRefParam ([ref]$myLocal) # Outputs Cannot process argument transformation on parameter 'MyRefParam'. Reference type is expected in argument.
Write-Host $myLocal # Outputs: oldValue
Is there another way to provide the ref type in this syntax? So far I've tried combinations of [ref] and [ref][string] in both the param definition and in the call - I can't get anything to let Powershell see that I am really passing a Ref
Thanks for any help!
CodePudding user response:
Use only a [ref]
type constraint in your parameter declaration, i.e. remove the additional [string]
type constraint (it doesn't do anything meaningful anyway, and arguably shouldn't even be allowed - see bottom section):
Function MyFunction ([ref] $MyRefParam) {
$MyRefParam.Value = "newValue"
}
$myLocal = 'oldVaue'
MyFunction -MyRefParam ([ref] $myLocal)
$myLocal # -> 'newvalue'
You cannot type a [ref]
instance: [ref]
isn't a keyword that modifies a parameter declaration (as ref
would be in C#), it is a type in its own right, System.Management.Automation.PSReference
, and its value-holding property, .Value
is of type object
, i.e. it can hold any type of object.
The upshot:
You cannot enforce a specific data type when a value is passed via
[ref]
.[1]Conversely, you're free to assign a value of any type to the
.Value
property of the[ref]
instance received.
Taking a step back:
[ref]
is primarily intended for supporting calls to .NET APIs withref
/out
parameters.Its use in pure PowerShell code is unusual and best avoided, not least due to the awkward invocation syntax.
That said, the difference in behavior with your double type constraint between positional and named parameter binding is certainly curious - see Mathias R. Jessen's excellent explanation in the comments.
However, the real problem is that you're even allowed to define a multi-type constraint (unusual in itself) that involves [ref]
, because there appears to be an intentional check to prevent that, yet it doesn't surface in parameter declarations.
You can make it surface as follows, however (run this directly at the command line):
# ERROR, because [ref] cannot be combined with other type constraints.
PS> [ref] [string] $foo = 'bar'
Cannot use [ref] with other types in a type constraint
That the same check isn't enforced in parameter declarations has been reported in GitHub issue #16146.
[1] Behind the scenes, a generic type that is non-public is used when an actual value is passed with a [ref]
cast (System.Management.Automation.PSReference<T>
), which is instantiated with whatever type the value being cast is. However, this generic type's .Value
property is still [object]
-typed, allowing for modifications to assign any type.