在PowerShell中调用泛型静态方法

Dav*_*ner 22 c# generics powershell powershell-2.0

如何在Powershell中调用自定义类的通用静态方法?

鉴于以下课程:

public class Sample
{
    public static string MyMethod<T>( string anArgument )
    {
        return string.Format( "Generic type is {0} with argument {1}", typeof(T), anArgument );
    }
}
Run Code Online (Sandbox Code Playgroud)

这被编译成一个程序集'Classes.dll'并加载到PowerShell中,如下所示:

Add-Type -Path "Classes.dll"
Run Code Online (Sandbox Code Playgroud)

调用MyMethod方法最简单的方法是什么?

Joh*_*hnC 14

正如@Athari所说,调用MyMethod的最简单方法是使用MakeGenericMethod.由于他实际上没有说明如何做到这一点,这里有一个经过验证的工作代码示例:

$obj = New-Object Sample

$obj.GetType().GetMethod("MyMethod").MakeGenericMethod([String]).Invoke($obj, "Test Message")
$obj.GetType().GetMethod("MyMethod").MakeGenericMethod([Double]).Invoke($obj, "Test Message")
Run Code Online (Sandbox Code Playgroud)

与输出

Generic type is System.String with argument Test Message
Generic type is System.Double with argument Test Message
Run Code Online (Sandbox Code Playgroud)


Ath*_*ari 13

您可以调用泛型方法,请参阅PowerShell中的非通用类上调用通用方法.

这不是直截了当的,你需要使用MakeGenericMethod函数.如果方法没有覆盖,则非常简单,如果方法没有覆盖则会变得更难.

以防万一,从那里复制粘贴的代码:

## Invoke-GenericMethod.ps1 
## Invoke a generic method on a non-generic type: 
## 
## Usage: 
## 
##   ## Load the DLL that contains our class
##   [Reflection.Assembly]::LoadFile("c:\temp\GenericClass.dll")
##
##   ## Invoke a generic method on a non-generic instance
##   $nonGenericClass = New-Object NonGenericClass
##   Invoke-GenericMethod $nonGenericClass GenericMethod String "How are you?"
##
##   ## Including one with multiple arguments
##   Invoke-GenericMethod $nonGenericClass GenericMethod String ("How are you?",5)
##
##   ## Ivoke a generic static method on a type
##   Invoke-GenericMethod ([NonGenericClass]) GenericStaticMethod String "How are you?"
## 

param(
    $instance = $(throw "Please provide an instance on which to invoke the generic method"),
    [string] $methodName = $(throw "Please provide a method name to invoke"),
    [string[]] $typeParameters = $(throw "Please specify the type parameters"),
    [object[]] $methodParameters = $(throw "Please specify the method parameters")
    ) 

## Determine if the types in $set1 match the types in $set2, replacing generic
## parameters in $set1 with the types in $genericTypes
function ParameterTypesMatch([type[]] $set1, [type[]] $set2, [type[]] $genericTypes)
{
    $typeReplacementIndex = 0
    $currentTypeIndex = 0

    ## Exit if the set lengths are different
    if($set1.Count -ne $set2.Count)
    {
        return $false
    }

    ## Go through each of the types in the first set
    foreach($type in $set1)
    {
        ## If it is a generic parameter, then replace it with a type from
        ## the $genericTypes list
        if($type.IsGenericParameter)
        {
            $type = $genericTypes[$typeReplacementIndex]
            $typeReplacementIndex++
        }

        ## Check that the current type (i.e.: the original type, or replacement
        ## generic type) matches the type from $set2
        if($type -ne $set2[$currentTypeIndex])
        {
            return $false
        }
        $currentTypeIndex++
    }

    return $true
}

## Convert the type parameters into actual types
[type[]] $typedParameters = $typeParameters

## Determine the type that we will call the generic method on. Initially, assume
## that it is actually a type itself.
$type = $instance

## If it is not, then it is a real object, and we can call its GetType() method
if($instance -isnot "Type")
{
    $type = $instance.GetType()
}

## Search for the method that:
##    - has the same name
##    - is public
##    - is a generic method
##    - has the same parameter types
foreach($method in $type.GetMethods())
{
    # Write-Host $method.Name
    if(($method.Name -eq $methodName) -and
    ($method.IsPublic) -and
    ($method.IsGenericMethod))
    {
        $parameterTypes = @($method.GetParameters() | % { $_.ParameterType })
        $methodParameterTypes = @($methodParameters | % { $_.GetType() })
        if(ParameterTypesMatch $parameterTypes $methodParameterTypes $typedParameters)
        {
            ## Create a closed representation of it
            $newMethod = $method.MakeGenericMethod($typedParameters)

            ## Invoke the method
            $newMethod.Invoke($instance, $methodParameters)

            return
        }
    }
}

## Return an error if we couldn't find that method
throw "Could not find method $methodName"
Run Code Online (Sandbox Code Playgroud)

  • 抱歉,我支持我的陈述 - "无法直接*在PowerShell中完成*".:-)令人敬畏的BTW工作......但实际上,PowerShell团队需要解决这个问题. (2认同)

Kei*_*ill 5

这是PowerShell的限制,无法直接在PowerShell V1或V2 AFAIK中完成.

顺便说一句,你的通用方法并不是通用的.不应该是:

public static string MyMethod<T>(T anArgument)
{ 
   return string.Format( "Generic type is {0} with argument {1}", 
                         typeof(T), anArgument.ToString()); 
} 
Run Code Online (Sandbox Code Playgroud)

如果您拥有此代码并希望从PowerShell中使用它,请避免使用泛型方法或编写非泛型C#包装器方法.

  • 你对这个方法是正确的..我为这个问题简化了它,可能有点太过分了:-) (2认同)