2

I have a powershell class that I need pass in a reference to a UInt64 Variable and return a value from the Method. I'm trying to do something like in the code below but it gives me a syntax error.

Maybe I don't need to add ?[ref]? because all variables are references by default in powershell including uint64? but i just wasn't sure if this is true.... If I follow normal C-language convention then the method gets a copy of the argument not the actual reference to the variable. I seem to recall something in C# about boxing and unboxing int types... boxing and unboxing c# int type How does it work for boxing Int for Powershell class methods?


class PowerHe11 {

    # Constructor
    PowerHe11() {
    }

    [void]AccessChannel(
        [UInt64]$ichan,
        [UInt64]$idata,
        [UInt64]$isize,
        [UInt64]$ireal,    
        [ref][UInt64]$otimeout,
        [ref][UInt64]$odata,
        [ref][UInt64]$ochan,
        [ref][UInt64]$osize

    ) {                  
        $osize = 64;  #Return this value to caller of method
    }
}

Error message is:

At C:\Users\wmoore\Documents\fpga\zynq_pl\run_ps1\Untitled1.ps1:13 char:11
+         [ref][UInt64]$otimeout,
+              ~~~~~~~~
Multiple type constraints are not allowed on a method parameter.
At C:\Users\wmoore\Documents\fpga\zynq_pl\run_ps1\Untitled1.ps1:14 char:14
+         [ref][UInt64]$odata,
+              ~~~~~~~~
Multiple type constraints are not allowed on a method parameter.
At C:\Users\wmoore\Documents\fpga\zynq_pl\run_ps1\Untitled1.ps1:15 char:14
+         [ref][UInt64]$ochan,
+              ~~~~~~~~
Multiple type constraints are not allowed on a method parameter.
At C:\Users\wmoore\Documents\fpga\zynq_pl\run_ps1\Untitled1.ps1:16 char:14
+         [ref][UInt64]$osize
+              ~~~~~~~~
Multiple type constraints are not allowed on a method parameter.
    + CategoryInfo          : ParserError: (:) [], ParentContainsErrorRecordException
    + FullyQualifiedErrorId : MultipleTypeConstraintsOnMethodParam

1

2 Answers 2

4

Your own answer shows the solution; let me add an explanation and background information:

about_Ref is the official help topic.

The short of it:

  • [ref]'s primary purpose is to support ref and out parameters in .NET methods; e.g., to call the System.Int32.TryParse method, use the following:

    • [int] $int = 0; [int]::TryParse('42', [ref] $int)

    • Note that the variable's type must match the type expected by the .NET method, and that the only way to create a variable in PowerShell is to also assign it a value (even if that value is irrelevant, as is the case with the out parameter at hand); the [ref] cast is required (analogous to the need to use ref or out in C#.

    • You may pass [ref] $null if you're not interested in the value being assigned by the method.

  • While you can use it in PowerShell code, doing so is awkward, as your example shows:

    • PowerShell code that receives a [ref] argument must refer to its value via the .Value property, as your answer shows.

    • You cannot type-constrain such a parameter, so you lose type safety.

      • Note that in function and scripts - as opposed to custom-class methods - it is syntactically allowed, but pointless to use both [ref] and a target data type (such as [ref][UInt64]$otimeout in your example), because the latter is effectively ignored; e.g.:

        • function foo { param([ref] [int] $p) $p.Value += '!' }; $bar = 'none'; foo ([ref] $bar); $bar

        • The call succeeds and $bar contains 'none!', which implies that the [int] type constraint was ignored.

    • Calling scripts and functions requires argument-mode syntax (shell-like, whitespace-separated arguments, bareword strings allowed), making the [ref]-cast invocation more awkward by requiring (...) around the argument, as shown above (foo ([ref] $bar)).

    • With respect to use in custom PowerShell classes, note that PowerShell's class support is - by design - not on par with that of OO-focused languages such as C#; that said, enhancements are planned; unfortunately, even the limited feature set still has problem as of PowerShell 7.1 - see GitHub issue #6652.


Technical background on [ref]:

[ref], unlike ref in C#, is not a language keyword: it is a type, namely [System.Management.Automation.PSReference] (whose type accelerator is [ref]).

As such, an expression such as [ref] $var is a cast that implicitly constructs a [ref] instance, which is required to bind to a [ref]-typed parameter.

Caveats:

  • There is parser magic involved in a [ref] cast, because you cannot directly use the normally equivalent [ref]::new($var) constructor expression as an alternative to cast [ref] $var.
    • PowerShell needs magic to know that $var refers to a variable object itself rather than to the variable's value, as is normally the case in an expression - and the latter is indeed what happens with [ref]::new($var) - $var's value is wrapped in a [ref] instance.
    • The true constructor equivalent of [ref] $var is [ref]::new((Get-Variable var))
# BROKEN: Do not use a constructor - [ref]::new($int) - 
#         in lieu of a cast - [ref] $int
PS> [int] $int = 0; $null = [int]::TryParse('42', [ref]::new($int)); $int
0 # !! $int was NOT updated, because its *value* was wrapped in a [ref] instance
  • It generally only makes sense to use a [ref] cast with a variable (object) operand.

    • PowerShell lets you pass any value as the operand, which technically works, but makes no sense unless you explicitly save the [ref] instance in a variable, pass it as the argument, and then use the [ref] instance's .Value property to access the updated value:
# Save a [ref] instance in a variable, pass it, then use .Value to get the 
# updated value.
PS> [ref] $intRef = 0; $null = [int]::TryParse('42', $intRef); $intRef.Value
42
Sign up to request clarification or add additional context in comments.

Comments

2
class powerhe11 {

# Constructor
powerhe11() {
}

    [void]AccessChannel(
        [ref]$ichan

    ) {         
         $ichan.value = 0xbeef;
         
         Write-Host("ichan: {0}" -f $ichan.value)
    }

}



[UInt64]$dude = 0

$ser = [gvmuart]::new()

$ser.AccessChannel([ref]$dude);


Write-Host -BackgroundColor Green "DUDE: $dude"

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.