0

How can I pass strings by reference to the parent scope?

This doesn't work since strings are not acceptable "values".

function Submit([ref]$firstName){ 
    $firstName.value =  $txtFirstName.Text 
}

$firstName = $null
Submit([ref]$firstName)
$firstName

Error: "Property 'value' cannot be found on this object; make sure it exists and is settable"

Doing this doesn't give an error but it doesn't change the variable either:

$firstName = "nothing"

function Submit([ref]$firstName){ 
    $firstName =  $txtFirstName.Text 
} 

Submit([ref]$firstName)
$firstName

Edit:

Doing the first code block by itself works. However when trying to do it in my script it returns the error again. I fixed it enough for it to assign the variable and do what I want but it still throws up an error and I was wondering how to fix that. I think it's because it doesn't like variable;es changing during a running session. Here is a link to my script

https://github.com/InconspicuousIntern/Form/blob/master/Form.ps1

4
  • I posted an "answer" below. I'll work with you there via the comments. Commented Jun 28, 2018 at 20:24
  • 1
    Why do you need to pass a string by reference? A function can output a string. Commented Jun 28, 2018 at 22:02
  • Your 1st snippet is conceptually correct and should work as-is (verified in v5.1, but I wouldn't expect v3 to act differently, unless there's a bug). That the 2nd snippet doesn't work is to be expected, because by assigning to $firstName directly you're creating a new [ref] instance. Commented Jun 28, 2018 at 22:38
  • I have multiple strings. Better ways of doing this are welcome. Doing the first code block by itself works. However doing it in my code returns the error again. I fixed it enough for it to assign the variable and do what I want but it still throws up an error and I was wondering how to fix that. Here is my whole script github.com/InconspicuousIntern/Form/blob/master/Form.ps1 Commented Jun 29, 2018 at 13:46

2 Answers 2

2

Your first snippet is conceptually correct and works as intended - by itself it does not produce the "Property 'Value' cannot be found on this object" error.

You're seeing the error only as part of the full script you link to, because of the following line:

$btnSubmit.Add_Click({ Submit })

This line causes your Submit function to be called without arguments, which in turn causes the $firstName parameter value to be $null, which in turn causes the error quoted above when you assign to $firstName.Value.

By contrast, the following invocation of Submit, as in your first snippet, is correct:

Submit ([ref] $firstName)  # Note the recommended space after 'Submit' - see below.

[ref] $firstName creates a (transient) reference to the caller's $firstName variable, which inside Submit binds to (local) parameter variable $firstName (the two may, but needn't and perhaps better not have the same name), where $firstName.Value can then be used to modify the caller's $firstName variable.


Syntax note: I've intentionally placed a space between Submit and ([ref] $firstName) to make one thing clearer:

The (...) (parentheses) here do not enclose the entire argument list, as they would in a method call, they enclose the single argument [ref] $firstName - of necessity, because that expression wouldn't be recognized as such otherwise.

Function calls in PowerShell are parsed in so-called argument mode, whose syntax is more like that of invoking console applications: arguments are space-separated, and generally only need quoting if they contain special characters.

For instance, if you also wanted to pass string 'foo', as the 2nd positional parameter, to Submit:

Submit ([ref] $firstName) foo

Note how the two arguments are space-separated and how foo needn't be quoted.


As for an alternative approach:

[ref]'s primary purpose is to enable .NET method calls that have ref / out parameters, and, as shown above, using [ref] is nontrivial.

For calls to PowerShell functions there are generally simpler solutions.

For instance, you can pass a custom object to your function and let the function update its properties with the values you want to return, which naturally allows multiple values to be "returned"; e.g.:

function Submit($outObj){ 
    $outObj.firstName = 'a first name'
}

# Initialize the custom object that will receive values inside
# the Submit function.
$obj = [pscustomobject] @{ firstName = $null }

# Pass the custom object to Submit.
# Since a custom object is a reference type, a *reference* to it
# is bound to the $outObj parameter variable.
Submit $obj

$obj.firstName # -> 'a first name'

Alternatively, you can just let Submit construct the custom object itself, and simply output it:

function Submit { 
    # Construct and (implicitly) output a custom
    # object with all values of interest.
    [pscustomobject] @{ 
        firstName = 'a first name' 
    } 
}

$obj = Submit

$obj.firstName # -> 'a first name'
Sign up to request clarification or add additional context in comments.

Comments

0

Please try this out and see if you are getting the same results? It is working for me, and I really did not change much.

$txtFirstName = [PSCustomObject]@{
    Text = "Something"
}

function Submit([ref]$var){ 
    $var.value =  $txtFirstName.Text 
}



$firstName = $null
Submit([ref]$firstName)
$firstName

2 Comments

Your answer should have been a comment: The OP's original code (1st snippet) should work, and the symptom he describes does not match the code. I suggest conducting the conversation in comments until the question no longer falls into the "a problem that can no longer be reproduced or a simple typographical error" closure category.
Thanks for the comment . . . I preferred an answer for the code formatting.

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.