powershellclassoop

PowerShell Class Method return Type as parameter (like JavaScriptSerializer.Deserialize)


I'm trying to build a Method inside a Class, that accepts the required return type as a parameter. An example of this, is the System.Web.Script.Serialization.JavaScriptSerializer Class, Deserialize Method. It takes two parameters, A JSON text and the return type that it is expected to return. If I were to do something like below, and provided the $JSONConect contains valid information about the Class I want to use, I get a proper Object of type [Test]

$JSSerializer   = New-Object System.Web.Script.Serialization.JavaScriptSerializer
$JSONObject     = [MyData]::New( )
If( $JSONContent -ne $null )
{
    $JSONObject     = $JSSerializer.Deserialize( $JSONContent, [MyData] )
}

I have tried building something this, but I get errors on the return type.

class Test
{
    [Type] TestType( [Type]$AParam )
    {
        Write-Host "Your provided the type  : $AParam"

        $Result = New-Object $AParam

        Write-Host "Your provided the value : $Result"

        Switch( $AParam )
        {
            "bool"
            {
                Write-Host "Your provided the type  : `"Boolean`""
                $Result = $True
            }
            "int"
            {
                Write-Host "Your provided the type  : `"Integer`""
                $Result = 2
            }
            "string"
            {
                Write-Host "Your provided the type  : `"String`""
                $Result = "Miep"
            }
            Default
            {
                Write-Host "Your provided the type  : `"Invalid!`""
                $Result = $null
            }
        }

        #$Result = [$AParam.GetType( )]
        #return $Result

        #return [Type]$( $AParam )
        return Write-Output -NoEnumerate $Result
    }
}

[bool]$tBool     = $False
[int]$tInt       = 0
[string]$tString = ""


$MyTest = [Test]::New( )

$tBool   = $MyTest.TestType( [bool] )
$tBool

exit
$tInt    = $MyTest.TestType( [int] )
$tInt

$tString = $MyTest.TestType( [string] )
$tString

#$MyTest.TestType( $tBool )
#$MyTest.TestType( $tInt )
#$MyTest.TestType( $tString )

The code above works partially, I can provide just a Type as parameter. But it will not return anything. As you can see, I have tried three ways to get this working, but I keep getting errors.

With the code as-is above, I get the following error:

Your provided the type  : bool
Your provided the value : False
Your provided the type: "Boolean"
Cannot convert value "True" to type "System.Type". Error: "Invalid cast from 'System.Boolean' to 'System.Type'."
At D:\ParameterTypePassingUsng.ps1:39 char:16
+         return Write-Output -NoEnumerate $Result
+                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidArgument: (:) [], RuntimeException
    + FullyQualifiedErrorId : InvalidCastIConvertible
 
False

Any suggestions on how to get this work, if it's at all possible? I cannot do this with CmdLets, because I just reworked my entire library to use classes instead of a collection of separate functions.


Solution

  • Hinted out by Mathias, what seemed you were looking for is known as Generic Methods in C# however PowerShell classes don't support this feature.

    The error is telling you that in your method signature you're stating that the method will take a Type as input and return a Type as output and you aren't fulfilling this contract since you're returning the instance of a type or $null (int, string, bool or $null).

    By changing the signature to return type of [object] the code would work properly:

    [object] TestType([Type] $AParam)
    

    You also will need to remove Write-Output -NoEnumerate, just return $result otherwise in PowerShell 7+ you will get unexpected return type of List<object>, for example:

    (Write-Output 1 -NoEnumerate).GetType() # List<object>
    

    It's also unclear what you want to do with $Result = New-Object $AParam, should be removed.

    In summary the definition should be:

    class Test {
        [Object] TestType([Type] $AParam) {
            Write-Host "You provided the type  : $AParam"
            $Result = $null
            switch ($AParam) {
                ([bool]) {
                    Write-Host "You provided the type  : `"Boolean`""
                    $Result = $True
                }
                ([int]) {
                    Write-Host "You provided the type  : `"Integer`""
                    $Result = 2
                }
                ([string]) {
                    Write-Host "You provided the type  : `"String`""
                    $Result = 'Miep'
                }
                Default {
                    Write-Host "You provided the type  : `"Invalid!`""
                }
            }
            return $Result
        }
    }
    

    Using Generics in C# the code could be something like:

    public static class Test
    {
        public static T? TestType<T>()
        {
            Type type = typeof(T);
            object? result = type switch
            {
                _ when type == typeof(bool) => true,
                _ when type == typeof(int) => 2,
                _ when type == typeof(string) => "Miep",
                _ => null
            };
    
            Console.WriteLine(
                $"You provided the type  : '{result?.GetType().Name ?? "Invalid!"}'");
    
            return (T?)result;
        }
    }