Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I call a parameterless generic method from Powershell v3?

For example I have a .NET object $m with the following method overloads:

PS C:\Users\Me> $m.GetBody

OverloadDefinitions
-------------------    
T GetBody[T]() 
T GetBody[T](System.Runtime.Serialization.XmlObjectSerializer serializer)  

If I try to invoke the parameterless method I get:

PS C:\Users\Me> $m.GetBody()
Cannot find an overload for "GetBody" and the argument count: "0".
At line:1 char:1
+ $m.GetBody()
+ ~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [], MethodException
    + FullyQualifiedErrorId : MethodCountCouldNotFindBest

I understand PowerShell v3.0 is supposed to work more easily with generics. Obviously I need to tell it somehow what type I want returned but I cannot figure out the syntax.

like image 616
Jack Ukleja Avatar asked Sep 12 '13 21:09

Jack Ukleja


2 Answers

It looks like you are trying to invoke a generic method.

In powershell this can be done by:

$nonGenericClass = New-Object NonGenericClass
$method = [NonGenericClass].GetMethod("SimpleGenericMethod")
$gMethod = $method.MakeGenericMethod([string]) 
# replace [string] with the type you want to use for T. 
$gMethod.Invoke($nonGenericClass, "Welcome!")

See this wonderful blog post for more info and additional examples.

For your example you could try:

$Source = @" 
public class TestClass
{
    public T Test<T>()
    {
        return default(T);
    }
    public int X;
}
"@ 

Add-Type -TypeDefinition $Source -Language CSharp 
$obj = New-Object TestClass

$Type  = $obj.GetType();
$m =  $Type.GetMethod("Test")
$g = new-object system.Guid
$gType = $g.GetType()
$gm = $m.MakeGenericMethod($gType)
$out = $gm.Invoke( $obj, $null)
#$out will be the default GUID (all zeros)

This can be simplified by doing:

$Type.GetMethod("Test").MakeGenericMethod($gType).Invoke( $obj, $null)

This has been testing in powershell 2 and powershell 3.

If you had a more detailed example of how you came across this generic method I would be able to give more details. I have yet to see any microsoft cmdlets return anything that give you generic methods. The only time this comes up is when custom objects or methods from c# or vb.net are used.

To use this without any parameters you can use Invoke with just the first parameter. $gMethod.Invoke($nonGenericClass)

like image 181
Chad Carisch Avatar answered Sep 21 '22 06:09

Chad Carisch


Calling a generic method on an object instance:

$instance.GetType().GetMethod('MethodName').MakeGenericMethod([TargetType]).Invoke($instance, $parameters)

Calling a static generic method (see also Calling generic static method in PowerShell):

[ClassType].GetMethod('MethodName').MakeGenericMethod([TargetType]).Invoke($null, $parameters)

Note that you will encounter an AmbiguousMatchException when there is also a non-generic version of the method (see How do I distinguish between generic and non generic signatures using GetMethod in .NET?). Use GetMethods() then:

([ClassType].GetMethods() | where {$_.Name -eq "MethodName" -and $_.IsGenericMethod})[0].MakeGenericMethod([TargetType]).Invoke($null, $parameters)

(Mind that there could be more than one method that match the above filter, so make sure to adjust it to find the one you need.)

Hint: You can write complex generic type literals like this (see Generic type of Generic Type in Powershell):

[System.Collections.Generic.Dictionary[int,string[]]]
like image 28
marsze Avatar answered Sep 22 '22 06:09

marsze