Home Dashboard Directory Help
Search

It isn't possible to pass null as null into a .NET method that has a parameter of type String by Kirk Munro


Status: 

Closed
 as Fixed Help for as Fixed


31
0
Sign in
to vote
Type: Bug
ID: 307821
Opened: 10/31/2007 12:04:37 PM
Access Restriction: Public
2
Workaround(s)
view
14
User(s) can reproduce this bug

Description

I'm logging this for Roman Kuzmin because he cannot access the Connect site right now. This comes from an issue he raised on the PowerShell newsgroup. It is not possible, at least as far as he or I can tell, to pass null as null into a .NET method that has a string parameter. For example, take a look at this class definition (provided by Roman):

using System;
namespace ClassLibrary1
{
public static class Class1
{
public static string Foo(string value)
{
if (value == null)
    return "Special processing of null.";
else
    return "'" + value + "' has been processed.";
}
}
}

If you build this and then try accessing it from PowerShell, how can you call Foo such that the "Special processing of null." section is used? If you call Foo with $null, an empty string is passed into the method. If you call it with a variable assigned to null, you get the same result. Anything else we have tried results in the same thing.

This isn't a problem when you own the source for the .NET class, but if you are using anything that you don't own the source to you can't trigger any special handling that would occur if null was passed in.

Is this a defect? It certainly seems like one. If you have a workaround or undocumented way that this can be done, it would be really appreciated.
Details
Sign in to post a comment.
Posted by Microsoft on 2/21/2012 at 6:39 PM
This has been fixed in Windows PowerShell 3.0. However, to pass null, you need to use [System.Management.Automation.Language.NullString]::Value

PS C:\> $content = @"
>> using System;
>> namespace ClassLibrary1
>> {
>> public static class Class1
>> {
>> public static ReturnValue Foo(string value)
>> {
>>    if (value == null)
>>     return ReturnValue.Null;
>>    else
>>     return ReturnValue.EmptyString;
>> }
>> }
>> public enum ReturnValue
>> {
>>     Null = 0,
>>     EmptyString = 1
>> }
>> }
>> "@
>>
PS C:\> Add-Type -TypeDefinition $content
PS C:\> [ClassLibrary1.Class1]::Foo($null)
EmptyString
PS C:\> [ClassLibrary1.Class1]::Foo([System.Management.Automation.Language.NullString]::Value)
Null
Posted by Tarun [Microsoft Connect] on 7/20/2010 at 5:57 PM
MCADMIN TESTING FOR TROUBLESHOOTING
Posted by Keith Hill MVP on 1/4/2010 at 6:02 PM
Seems like it would be nice to have some syntax for declaring to PowerShell that you don't want any automatic type coercion e.g. $obj.DoSomething($a, $b, [nocast]$null)
Posted by andrey.anastasov on 1/29/2009 at 3:16 AM
The best workaround I can think of is by using .Net reflection. For example, instead of:
    $obj.DoSomething($a,$b,$null)
...you can do:
    $params = @($a,$b,$null)
    $obj.GetType().GetMethod("DoSomething").Invoke($obj, $params)
Posted by Matthew Hobbs on 6/13/2008 at 9:45 AM
Similarly you can't set a string property on a .NET object to null.
Posted by Roman Kuzmin on 5/20/2008 at 3:09 AM
Yet another real case:

System.IO.FileInfo.Replace Method (String, String destinationBackupFileName)
System.IO.FileInfo.Replace Method (String, String destinationBackupFileName, Boolean)

Second parameter:
destinationBackupFileName
The name of a file with which to create a backup of the file described by the destFileName parameter.
...
Pass a null reference (Nothing in Visual Basic) to the destBackupFileName parameter if you do not want to create a backup of the file being replaced.

The last case is impossible from PowerShell because null cannot be passed where string is expected.
Posted by Scott Yost [MSFT] on 11/25/2007 at 9:22 AM
This one came up on the internal DL as well - it prevents you from calling x509certificatecollection.import() with a $null password. You have to use inline C# to do it.
Sign in to post a workaround.
Posted by andrey.anastasov on 1/29/2009 at 3:37 AM
You can work around the problem by using .Net reflection. For example, instead of:
    $obj.DoSomething($a,$b,$null)
...you can do:
    $params = @($a,$b,$null)
    $obj.GetType().GetMethod("DoSomething").Invoke($obj, $params)
Posted by patton1234 on 2/12/2008 at 3:51 AM
One way you can workaround the problem is to write a cmdlet to wrap the .Net call you need to make. This is a signifant overhead for what i want to do.

I have included the code to fix my problem below.

using System;
using System.Collections.Generic;
using System.Text;
using System.Management.Automation;
using System.Collections;
using Microsoft.TeamFoundation.VersionControl.Client;

namespace WindowsPowerShell1
{
    [Cmdlet(VerbsCommon.Get, "Label", SupportsShouldProcess = true)]
    public class GetLabel : Cmdlet
    {
        private string label;
        private string scope;
        private VersionControlServer vcs;
        private string user;
        private bool includeItems;

        #region Parameters
        [Parameter(Position = 0,
            Mandatory = true,
            ValueFromPipelineByPropertyName = true,
            HelpMessage="The label name - * is wildcard")]
        [ValidateNotNullOrEmpty]
        public string Label
        {
            get { return label; }
            set { label = value; }
        }
        [Parameter(Position = 1,
         Mandatory = true,
         ValueFromPipelineByPropertyName = true,
         HelpMessage = "Scope")]
        [ValidateNotNullOrEmpty]
        public string Scope
        {
            get { return scope; }
            set { scope = value; }
        }
        [Parameter(Position = 2,
         Mandatory = true,
         ValueFromPipelineByPropertyName = true,
         HelpMessage = "VersionControlServer")]
        [ValidateNotNull]
        public VersionControlServer Vcs
        {
            get { return vcs; }
            set { vcs = value; }
        }
        [Parameter(Position = 3,
             Mandatory = false,
             ValueFromPipelineByPropertyName = true,
             HelpMessage = "VersionControlServer")]
        public string User
        {
            get { return user; }
            set { user = value; }
        }
        [Parameter(Position = 4,
             Mandatory = true,
             ValueFromPipelineByPropertyName = true,
             HelpMessage = "IncludeItems")]
        public bool IncludeItems
        {
            get { return includeItems; }
            set { includeItems = value; }
        }
        #endregion

        protected override void ProcessRecord()
        {
            try
            {
                WriteObject(Vcs.QueryLabels(label, Scope, User,
IncludeItems));
            }
            catch (Exception)
            {
            }
        }
    }
}