To complement Mathias R. Jessen's helpful answer, which explains the problem well and offers an effective solution:
PowerShell's casts are not only syntactically more convenient than constructor calls, but also more flexible when it comes to on-demand type conversions.
Indeed, using a cast instead of calling a constructor, via the static ::new()
method, does work:
using namespace System.Collections.Generic
class Thing { [string] $Name; Thing([string] $name) { $this.Name = $name } }
# Both of the following work:
# Single [Thing] instance.
$list = [List[Thing]] [Thing]::new('one')
# Multiple [Thing] instances, as an array, via the grouping operator, (...)
# @(...), the array subexpression operator, works too, but is unnecessary.
$list = [List[Thing]] ([Thing]::new('one'), [Thing]::new('two'))
PowerShell's automatic type conversions, as also used in casts:
Unfortunately, as of this writing the rules aren't documented, but a comment in the source-code provides a high-level overview, as does the (pretty low-level) ETS type converters documentation, which can be summarized as follows, in descending order of precedence:
First, engine-internal, fixed conversion rules may be applied (see source-code link above).
A notable internal rule concerns to-string conversions: while any .NET type supports it by an explicit call to its .ToString()
method (inherited from the root of the object hierarchy, System.Object
), PowerShell applies custom rules:
If a type has a culture-sensitive .ToString(<IFormatProvider>)
overload, PowerShell passes the invariant culture deliberately, to achieve a culture-invariant representation, whereas a direct .ToString()
call would yield a culture-sensitive representation - see this answer for details; e.g., in a culture where ,
is the decimal mark, [string] 1.2
returns '1.2'
(period), whereas (1.2).ToString()
returns '1,2'
(comma).
Collections, including arrays, are stringified by concatenating their (stringified) elements with a space as the separator (by default, can be overridden with preference variable $OFS
); e.g., [string] (1, 2)
returns 1 2
, whereas (1, 2).ToString()
returns merely System.Object[]
.
Also, PowerShell converts freely:
- between different number types (when possible).
- between numbers and strings (in a culture-invariant manner, recognizing only
.
as the decimal mark when converting from a string).
- and allows any data type to be converted to (interpreted as) as Boolean - see the bottom section of this answer for the rules.
- from a scalar to an array / list type (e.g.,
[int[]] 42
creates a single-element array, and [System.Collections.Generic.List[int]] 42
creates a single-element list; small caveat: there are bugs with certain list types - see this answer).
Next, TypeConverter
or (PSTypeConverter
) classes that implement custom conversions for specific types are considered.
If the input type is a string ([string]
), a static ::Parse()
method is considered, if present: first, one with a culture-sensitive signature, ::Parse(<string>, <IFormatProvider>)
, in which case the invariant culture is passed, and, otherwise one with signature ::Parse(<string>)
.
Next, a single-argument constructor is considered, if the input type matches the argument's type or is convertible to it.
If an implicit or explicit conversion operator exists for conversion between the input and the target type.
Finally, if the input object implements the System.IConvertible
interface and the target type is a supported-by-the-implementation primitive .NET type except [IntPtr]
and [UIntPtr]
or one of the following types: [datetime]
, [DBNull]
, [decimal]
.