2010-11-22 39 views
17

¿Cómo se llama a un método estático genérico de una clase personalizada en Powershell?Llamar al método estático genérico en PowerShell

Dada la siguiente clase:

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

Y esto se compila en un ensamblado 'Classes.dll' y se carga en PowerShell como esto:

Add-Type -Path "Classes.dll" 

¿Cuál es la forma más fácil de llamar a la MiMetodo ¿método?

Respuesta

10

Puede llamar a los métodos genéricos, consulte la publicación Invoking Generic Methods on Non-Generic Classes in PowerShell.

Esto no es sencillo, debe usar la función MakeGenericMethod. Es bastante simple si el método no tiene sustituciones, se vuelve más difícil si lo hace.

Por si acaso, código de copia-pegar desde allí:

## 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" 
+2

Lo siento pero resisto mi declaración - 'no se puede hacer * directamente * en PowerShell'. :-) Awesome work-around BTW ... pero realmente, el equipo de PowerShell necesita arreglar este agujero. –

+0

Acuerde con Keith que sería bueno tener soporte incorporado para esto, pero como esta es una solución (incluso si no es directa), esta respuesta se lleva el tic. –

+0

El código de muestra largo no es necesario para direccionar OP, MakeGenericMethod es suficiente. – JohnC

5

Esto es una limitación de PowerShell y no se puede hacer directamente en PowerShell V1 o V2 ​​AFAIK.

Por cierto, su método genérico no es realmente genérico. ¿No debería ser:

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

Si es el propietario de este código y desea utilizarlo de PowerShell, evitar los métodos genéricos o escribir un método de envoltura en C# no genérico.

+0

Tienes razón sobre el método ... Lo simplifiqué para la pregunta, y probablemente fui demasiado lejos :-) –

1

manera rápida, si no hay conflictos de nombres:

[Sample]::"MyMethod"("arg") 
11

La forma más fácil para llamar MiMetodo es, como @Athari dice, para usar MakeGenericMethod. Desde que en realidad no muestran cómo hacerlo, aquí es un ejemplo de código de trabajo verificadas:

$obj = New-Object Sample 

$obj.GetType().GetMethod("MyMethod").MakeGenericMethod([String]).Invoke($obj, "Test Message") 
$obj.GetType().GetMethod("MyMethod").MakeGenericMethod([Double]).Invoke($obj, "Test Message") 

con la salida

Generic type is System.String with argument Test Message 
Generic type is System.Double with argument Test Message 
+0

wow, útil. Gracias – Pisu

2

La buena noticia es v3 PowerShell es mucho mejor en la unión a métodos genéricos (y reificarlos?) y a menudo no tienes que hacer nada especial, pero llámalo como lo harías con un método normal. No puedo especificar todos los criterios para los que esto funciona ahora, pero en mi experiencia ciertas situaciones con parámetros genéricos aún requieren soluciones incluso en PowerShell v4 (tal vez sea la existencia o sobrecargas o algo similar).

De manera similar, a veces también tengo problemas para pasar un parámetro genérico a un método ... por ejemplo, pasar un parámetro Func<T1, T2, TResult>.

Una solución alternativa que para mí es mucho más simple que MakeGenericMethod u otros enfoques es que sólo hay que poner una clase rápida C# envoltorio directamente en mi guión, y sea C# clase toda la asignación genérica ...

Aquí es un ejemplo de este enfoque que envuelve el método Enumerable.Zip. En este ejemplo, mi clase C# no es genérica, pero eso no es estrictamente necesario.

Add-Type @' 
using System.Linq; 
public class Zipper 
{ 
    public static object[] Zip(object[] first, object[] second) 
    { 
     return first.Zip(second, (f,s) => new { f , s}).ToArray(); 
    } 
} 
'@ 
$a = 1..4; 
[string[]]$b = "a","b","c","d"; 
[Zipper]::Zip($a, $b); 

Esto produce:

f s 
- - 
1 a 
2 b 
3 c 
4 d 

Estoy seguro de que hay mejores maneras de PowerShell a "zip" dos matrices pero se entiende la idea. El verdadero desafío que esquivé aquí fue tener un tercer parámetro codificado (en la clase C#) en Zip, así que no tuve que descubrir cómo pasar ese Func<T1, T2, TResult> (Tal vez también hay una forma de hacerlo con PowerShell). ?).

Cuestiones relacionadas