14

例如,我有一个$m具有以下方法重载的 .NET 对象:

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

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

如果我尝试调用无参数方法,我会得到:

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

我了解 PowerShell v3.0 应该更容易使用泛型。显然我需要以某种方式告诉它我想要返回什么类型,但我无法弄清楚语法。

4

4 回答 4

16

看起来您正在尝试调用通用方法

在 powershell 中,这可以通过以下方式完成:

$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!")

有关更多信息和其他示例,请参阅这篇精彩的博客文章。

对于您的示例,您可以尝试:

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

这可以通过执行以下操作来简化:

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

这已经在 powershell 2 和 powershell 3 中进行了测试。

如果您有一个更详细的示例来说明您是如何遇到这种通用方法的,我将能够提供更多详细信息。我还没有看到任何 microsoft cmdlet 返回任何给你通用方法的东西。唯一出现这种情况是在使用来自 c# 或 vb.net 的自定义对象或方法时。

要在没有任何参数的情况下使用它,您可以仅使用第一个参数来使用 Invoke。$gMethod.Invoke($nonGenericClass)

于 2013-09-12T22:41:18.113 回答
4

在对象实例上调用泛型方法:

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

调用静态泛型方法(另请参阅在 PowerShell 中调用泛型静态方法):

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

请注意,AmbiguousMatchException当还存在方法的非泛型版本时,您会遇到一个(请参阅如何在 .NET 中使用 GetMethod 区分泛型和非泛型签名?)。然后使用GetMethods()

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

(请注意,与上述过滤器匹配的方法可能不止一种,因此请确保对其进行调整以找到您需要的方法。)

提示:您可以像这样编写复杂的泛型类型文字(请参阅Powershell 中的泛型类型的泛型类型):

[System.Collections.Generic.Dictionary[int,string[]]]
于 2016-04-14T08:28:50.190 回答
2

要从 Powershell v3 调用具有重载的(无参数)泛型方法,如 OP 示例中所示,请使用 @Chad Carisch 提供的参考中的脚本 Invoke-GenericMethod.ps1,Invoking Generic Methods on Non-Generic Classes in PowerShell

它应该看起来像

Invoke-GenericMethod $m GetBody T @()

这是我正在使用的经过验证的工作代码示例:

[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.Practices.ServiceLocation") | Out-Null
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.Practices.SharePoint.Common") | Out-Null

$serviceLocator = [Microsoft.Practices.SharePoint.Common.ServiceLocation.SharePointServiceLocator]::GetCurrent()

# Want the PowerShell equivalent of the following C#
# config = serviceLocator.GetInstance<IConfigManager>();

# Cannot find an overload for "GetInstance" and the argument count: "0".
#$config = $serviceLocator.GetInstance()

# Exception calling "GetMethod" with "1" argument(s): "Ambiguous match found."
#$config = $serviceLocator.GetType().GetMethod("GetInstance").MakeGenericMethod([IConfigManager]).Invoke($serviceLocator)

# Correct - using Invoke-GenericMethod
$config = C:\Projects\SPG2013\Main\Scripts\Invoke-GenericMethod $serviceLocator GetInstance Microsoft.Practices.SharePoint.Common.Configuration.IConfigManager @()

$config.CanAccessFarmConfig

这是我没有尝试过但更新且正在积极维护的替代脚本Invoke Generic Methods from PowerShell

于 2014-03-29T18:44:17.557 回答
1

marsze 的有用答案包含有关调用泛型方法的重要一般信息,但让我专门解决调用无参数方法的方面,如所问:

正如问题中所暗示的:

  • 在 PSv3+ 中,PowerShell 可以从传递给泛型方法的参数值(参数)推断类型,
  • 根据定义,它不能与无参数的泛型方法一起使用,因为没有任何东西可以从中推断出类型

从 Windows PowerShell v5.1 / PowerShell Core v6.1.0 开始,PowerShell没有允许您在此方案中显式指定类型的语法

但是,GitHub 上有一个建议来增强PowerShell Core中的语法,该建议原则上已获批准,但正在等待社区实施。

现在,必须使用反射

# Invoke $m.GetBody[T]() with [T] instantiated with type [decimal]
$m.GetType().GetMethod('GetBody', [type[]] @()).
    MakeGenericMethod([decimal]).
      Invoke($m, @())
  • .GetMethod('GetBody', [type[]] @())由于传入了一个空的参数类型数组,因此明确地找到了 的无参数重载。.GetBody()

  • .MakeGenericMethod([decimal])用示例类型实例化方法[decimal]

  • .Invoke($m, @())$m然后在不带参数的输入对象 ( ) 上调用类型实例化的方法( @(),空数组)。

于 2018-07-08T20:19:34.150 回答