Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I instantiate an object in PowerShell and set a property in a single command?

I know how to instantiate object in PowerShell but I am struggling to find out if its possible to instantiate an object and set one of its properties in the same command.

The following commands work of course:

$f = &{$f=[System.Windows.Forms.Form]::new();$f.Text="Aloha!";$f}
[System.Windows.Forms.Application]::Run($f)

But combining these two commands into one does not work neither in Windows PowerShell nor in PowerShell 7.

[System.Windows.Forms.Application]::Run(&{System.Windows.Forms.Form]::new();$f.Text="Aloha!";$f})

I have no idea why the parser thinks that a ) is missing.

like image 814
PeterXX Avatar asked Nov 01 '25 02:11

PeterXX


2 Answers

Your problem is unrelated to your specific code and boils down to a syntax problem:

  • A construct starting with &, the call operator, is a command, and therefore parsed in argument mode.

  • As such, in order to participate in an expression such as a method call, which is parsed in expression mode, it must be enclosed in (...), the grouping operator, in addition to the always-required ( and ) instances that enclose the method parameter value(s).

Therefore (note the extra ( and ) around & { ... }):

[System.Windows.Forms.Application]::Run(
  (& {
    [System.Windows.Forms.Form]::new();$f.Text="Aloha!";$f
  })
)

Note: The code is spread across multiple lines purely for readability; it works equally on a single line.

However, you can simplify the call by replacing (& { ... }) with $( ... ), i.e. with the subexpression operator (which is itself an expression and therefore doesn't need the extra (...) enclosure):

[System.Windows.Forms.Application]::Run(
  $(
    [System.Windows.Forms.Form]::new();$f.Text="Aloha!";$f
  )
)

Note:

  • Both & { ... } (i.e. invocation of a script block in a child scope via &) and $( ... ) support enclosing multiple statements.

  • However, the crucial difference is that & { ... } produces streaming output, i.e. emits output objects as they're being created, whereas $(...) collects all output up front first.

    • Since streaming output cannot be taken advantage of in a method call, there is no advantage to using & { ... } here (which requires the additional (...) enclosure), so $( ... ) is the simpler solution.

    • A secondary difference is that & runs a given script block ({ ... }) in a child scope, whereas $( ... ) runs directly in the caller's scope (as does @( ... ), the array-subexpression operator).
      However, you have the option to run a script block directly in the caller's scope too, by using . , the dot-sourcing operator in lieu of & (. { ... }).


Taking a step back:

  • PowerShell allows you to initialize objects by casting a hashtable @{ ... }) with the desired property values, provided that the target type has a parameterless public constructor.
    See this answer for more information.

  • Since [System.Windows.Forms.Form] meets this criterion, you can simplify your call as follows; note that since such a cast is an expression, you do not need to enclose it in extra (...) in order to use it as a method argument (since the expression doesn't contain ,):

# Note the use of a cast and a hashtable with the desired property value(s).
[System.Windows.Forms.Application]::Run(
  [System.Windows.Forms.Form] @{ Text = "Aloha!" }
)

As you point out, you can make the code more concise if you add a using namespace statement at the top of your script, in addition to loading the assembly either via Add-Type -AssemblyName or via using assembly.[1]
To put it all together in a self-contained manner:

Add-Type -AssemblyName System.Windows.Forms # Load the assembly
using namespace System.Windows.Forms # Allow referencing its types by name only.

[Application]::Run([Form] @{ Text = "Aloha!" })

[1] Sadly, the using assembly form is broken in PowerShell (Core) 7 as of v7.5.x - see GitHub issue #11856.

like image 65
mklement0 Avatar answered Nov 02 '25 17:11

mklement0


Check (and think about) [System.Windows.Forms.Application]::Run:

OverloadDefinitions
-------------------
static void Run()
static void Run(System.Windows.Forms.Form mainForm)
static void Run(System.Windows.Forms.ApplicationContext context)

Then, the following one-liner should work, using the Grouping operator ( ):

 [System.Windows.Forms.Application]::Run((& {$a=[System.Windows.Forms.Form]::new();$a.Text="Aloha!";$a}))

As an alternative, try the Subexpression operator $( ) as follows:

 [System.Windows.Forms.Application]::Run($(& {$a=[System.Windows.Forms.Form]::new();$a.Text="Aloha!";$a}))

Note: don't forget to run

if ( -not ("System.Windows.Forms.Form" -as [type]) ) {
    Add-Type -AssemblyName System.Windows.Forms
}

before using [System.Windows.Forms.Application] type…

like image 20
JosefZ Avatar answered Nov 02 '25 17:11

JosefZ



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!