在PowerShell中调用generics静态方法

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

鉴于以下课程:

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

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

 Add-Type -Path "Classes.dll" 

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

您可以调用generics方法,请参阅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 (ie: 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" 

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

 $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 

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

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

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

如果您拥有此代码并希望在PowerShell中使用它,请避免使用generics方法或编写非genericsC#包装器方法。

好消息是PowerShell v3在绑定generics方法方面要好得多(并且通过它们来实现?)而且你通常不需要做任何特殊的事情,而是像普通方法一样调用它。 我无法指定现在可以使用的所有条件,但根据我的经验,某些具有通用参数的情况仍然需要解决方法,即使在PowerShell v4中(可能是存在或重载或类似的东西)。

类似地,我有时也会将generics参数传递给方法…例如传递Func参数。

对我来说,一个解决方法比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); 

这会产生:

  fs - - 1 a 2 b 3 c 4 d 

我确信有更好的PowerShell方法来“拉”两个arrays,但你明白了。 我在这里提出的真正挑战是对Zip进行硬编码(在C#类中)第3个参数,所以我没有弄清楚如何传入Func (也许有一个PowerShell的方式也是如此?)。

快速方式,如果没有名称冲突:

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