2010-11-22 16 views
17

Powershellでカスタムクラスの汎用静的メソッドを呼び出すにはどうすればよいですか?PowerShellで汎用静的メソッドを呼び出す

次のクラスを考えると

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

そして、これは次のようにPowerShellのにアセンブリ「Classes.dll」にコンパイルしてロードされます。

Add-Type -Path "Classes.dll" 

MyMethodはを呼び出すための最も簡単な方法は何方法?

答えて

10

一般的なメソッドを呼び出すことができます。投稿Invoking Generic Methods on Non-Generic Classes in 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" 
+2

申し訳ありませんが、私は私の声明に立っています - '直接*できません* PowerShell'です。 :-)素晴らしい作業を進めています...しかし、実際、PowerShellチームはこの穴を修正する必要があります。 –

+0

これは組み込みのサポートがあることがKeithと合意していますが、これは解決策であるため(直接的でない場合でも)、この回答は目立ちます。 –

+0

長時間のコードサンプルは、OPに対処する必要はなく、MakeGenericMethodで十分です。 – JohnC

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()); 
} 

あなたはこのコードを所有し、PowerShellのからそれを使用したい場合は、一般的な方法を回避または非ジェネリックC#のラッパー・メソッドを記述します。

+0

あなたは正しい方法です。私は質問のために簡略化し、多分あまりにも遠くに行きました:-) –

1

高速道、何の名前の競合がない場合:

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

がMyMethodはを呼び出すための最も簡単な方法は@Athariとして、あります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") 

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

うわー、便利。ありがとうございました – Pisu

2

良いニュースは、PowerShellのv3のは、一般的な方法への結合ではるかに優れている:彼は実際にそれを行う方法を示していないので、ここでの検証作業のコードサンプルです(そしてそれらをreified?)あなたはしばしば特別な何かをする必要はありませんが、通常の方法と同じようにそれを呼び出す。これが現在動作するすべての基準を指定することはできませんが、私の経験では一般的なパラメータを使用した特定の状況では、PowerShell v4(おそらくその存在または過負荷など)でも回避策が必要です。

同様に、Func<T1, T2, TResult>パラメータを渡すなど、ジェネリックパラメータをメソッドに渡すときに問題が発生することがあります。

一つの仕事私の周りにMakeGenericMethodまたは他のアプローチは、ちょうど私のスクリプトで直接、迅速C#のラッパークラスを入れて、C#、すべての一般的なマッピングを整理できるようにするよりも、はるかに簡単であることを...

ここEnumerable.Zipメソッドをラップするこのアプローチの例です。この例では、C#クラスはまったく汎用ではありませんが、厳密には必要ではありません。

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); 

これが生成します。

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

私はより良いPowerShellの方法が「郵便番号」二つの配列があると確信していますが、アイデアを得ます。私がここで避けた本当の挑戦は、(C#クラスの)3番目のパラメータをZipにハードコードしていたので、その方法を理解する必要はありませんでした。Func<T1, T2, TResult>(PowerShellの方法もあります?)。

関連する問題