I have some PowerShell code where I am invoking a .NET method which expects a class to be passed as a parameter.
The method accepts a null value as the parameter, and in C# you'd call it as follows:
var T = New Foo(null)
If translating this into PowerShell, one would try:
$T = New-Object Foo($null)
But that returns the following error
New-Object : Constructor not found. Cannot find an appropriate constructor for type Foo
Funilly enough, the exact same error is returned if I call it as:
$T = New-Object Foo
So first question is why? Does .NET see $null as the absence of something, as oppose to the type null from .NET? Is that why the two instructions above return the same error?
Now, I found a way around to this problem as follows:
$T = New-Object Foo(@($null))
This works just fine, but... why?
(@($Null)).GetType() returns object[]
(@()).GetType() also return object[], but if I run:
$T = New-Object Foo(@())
I still get:
New-Object : Constructor not found. Cannot find an appropriate constructor for type Foo
So, second question: What is the difference between @(), @($null) and $null?
Also, and last question, this is something else I don't understand.
Let's say I have another .NET method as Bar(string, Foo)
If I call:
$B = New-Object Bar("s", $null)
This works just fine, without having to convert it into an object[] or anything alike.
So the third and last question is: why does the number of parameters affect whether it needs to be passed as an array or not?
My guess would be that if I was to cast it into an array I would call it as
$B = New-Object Bar(@("s", $null))
Meaning that even though the method expects two parameters, I'm passing it only one as an array of objects and then it automatically maps each of the array items onto the parameters, but that still doesn't quite provide an answer for the first two questions :)
EDIT
I know that @() is an empty array and that @($null) is an array with one element which happens to be null, so that's not what I'm asking.
However, if calling:
$T = New-Object Foo($null)
Makes Foo's constructor see no parameter as being passed, why does calling @() vs calling @($null) differ? Surely @($null) should be the same as @() in this case, the absence of a parameter as it gets seen in the .NET method.
Why does $null sometimes mean something and sometimes nothing at all?
Based on your edit, you have a good understanding of the differences between $null and @($null).
Unlike a normal call to a .Net method, calling the constructor requires calling the New-Object cmdlet first. The syntax for New-Object is:
New-Object [-TypeName] <string> [[-ArgumentList] <Object[]>] [-Property <IDictionary>]
Let's think for a moment how New-Object works. Using reflection, New-Object will call TypeName.GetConstructors() and find candidate constructors that can accept n arguments where n is the number of arguments in ArgumentList.
If you call New-Object like this:
New-Object -TypeName Foo
Then ArgumentList has the value $null. You didn't pass $null, but that's it's value anyway. If ArgumentList is $null, there are no arguments. In this example, that's obvious, you didn't specify -ArgumentList. If you write:
New-Object -TypeName Foo -ArgumentList $null
This ends up with the same end result - no argument list. ArgumentList expects an [object[]], and $null is a perfectly valid value for [object[]], but it means there are no arguments.
If you write:
New-Object -TypeName Foo -ArgumentList @($null)
Now you are passing an [object[]] with a single value, the value $null, and everything works as you expect.
This is undoubtedly confusing, but it helps to think of how you might implement your own C# function that calls another function and accepts a param array. If your param array is null, you received no extra arguments.
New-Object can be even more confusing in some cases. I was personally tripped up by overloaded constructors, one taking an array, the other taking multiple arguments. In C#, the constructors were something like:
Foo(object[] array); // #1
Foo(string s, int i); // #2
And my incorrect attempt to call constructor #1:
New-Object -TypeName Foo -ArgumentList @($array)
This calls constructor #2 because New-Object always expects an [object[]] to pass multiple arguments and rarely do you want to call a constructor with a single array argument. The correct PowerShell would be:
New-Object -TypeName Foo -ArgumentList (,$array)
Because of this confusion, I added a new way to call constructors in PowerShell V5. It's available as a very early preview now (http://www.microsoft.com/en-us/download/details.aspx?id=42936), the syntax looks like a static method call:
[Foo]::new($name, $count)
With this new syntax, calling constructors is no longer confusing, and as a side benefit, it's also an order of magnitude faster than calling New-Object.
EDIT: I corrected the example with overloaded constructors.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With