Check out User.cs
down below. It's a pretty simple class with three constructors: the default (parameterless) one, and one each taking the user name, and id. PowerShell will cast using the constructors of a type, so in this case, you can cast either a string or a number --or a hashtable-- to User
.
We can add this type to our PowerShell session using Add-Type
:
Add-Type -path User.cs
The most interesting of PowerShell's special casting powers is the hashtable cast. Any class that has a default (parameterless) constructor can be created by casting a hashtable of (some of) it's settable properties:
[user]@{
Id = 1
Name = "Jaykul"
}
Id Name OAuthUri
-- ---- --------
1 Jaykul
In the case of this particular User
class, when we cast a number, like [User]2
it's the same as calling [User]::new(2)
, but the results of either are a little surprising:
Id Name OAuthUri
-- ---- --------
0 2
The number was used for the Name
value, instead of the Id
that we expected...
The reason for this is actually straightforward, but it's due to a combination of reasons that aren't intuitive.
The bottom line is that the numeric constructor we might have thought we were calling accepts a uint
(an unsigned int), rather than a signed int
, and literal numbers in PowerShell are always [int]
(or [double]
if they have a decimal place).
PowerShell (well, programming languages in genera) won't cast implicitly if it looses information. Implicit casting is when you try to pass a value to a method (or function) that accepts a different type of value. Explicit casting is when you specify the type by writing it out.
So for instance, a int
will cast implicitly to a double
, but not the other way around, because you would loose the fractional value. You can still cast it explicitly, by just specifying [double]
.
PowerShell simply won't call the method that accepts an unsigned int unless you specifically cast the number to an unsigned int. As far as syntax, you can double-cast it:
[User][uint]2
Or you can call the constructor more explicitly:
[User]::new([uint]2)
Either way, you'll get a user with an Id but no Name.
Id Name OAuthUri
-- ---- --------
2
Of course, the other part of the puzzle is why you get an unexpected result, instead of an error like:
"InvalidArgument: Cannot convert the "2" value of type "System.Int32" to type "User".
The reason is yet another PowerShell casting oddity: PowerShell can (and will) cast anything to a string implicitly, by calling it's ToString()
method. That's really convenient in a shell, where you regularly want to display things as strings, but sometimes... well, something like this happens.
In other words, PowerShell will implicitly cast an integer to string
, but will not cast implicitly it to uint
-- so you get an unexpected constructor and result.