【问题标题】:How do I call a parameterless generic method from Powershell v3?如何从 Powershell v3 调用无参数泛型方法?
【发布时间】:2013-09-12 21:50:27
【问题描述】:

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

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 应该更容易使用泛型。显然我需要以某种方式告诉它我想要返回什么类型,但我无法弄清楚语法。

【问题讨论】:

    标签: powershell powershell-3.0


    【解决方案1】:

    您似乎正在尝试调用generic method

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

    有关更多信息和其他示例,请参阅 this 精彩博文。

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

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

    【讨论】:

    • 是的,它是问题标题中所述的通用方法:) 我将更新以澄清这是使用 new-object 创建的 .NET 对象。 Lees 的帖子还声称在 PSv3 中你不需要任何丑陋的东西……问题是我要调用的方法没有输入参数,因此无法推断 T。
    • 我很确定您仍然必须在这种情况下使用反射。 V3 改进了泛型的使用,但仍有一些粗糙的地方。
    • V3 仅在创建通用对象(例如通用列表)方面有所改进。我觉得如果你真的需要泛型,你可能应该考虑编写一个控制台应用程序,它利用 powershell 函数,但在 C# 或 VB.net 中处理泛型。
    • @Schneider ,我很想知道这是否适合您。我做了一些研究,发现 psv3 并没有改进泛型方法,只是创建了泛型对象。
    • 此答案和代码示例不回答具有重载方法的 OP。但是,引用的文章确实解释了如何处理重载。
    【解决方案2】:

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

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

    调用一个静态泛型方法(另见Calling generic static method in PowerShell):

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

    请注意,当该方法还存在非泛型版本时,您将遇到AmbiguousMatchException(请参阅How do I distinguish between generic and non generic signatures using GetMethod in .NET?)。然后使用GetMethods()

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

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

    提示:您可以像这样编写复杂的泛型类型文字(参见Generic type of Generic Type in Powershell):

    [System.Collections.Generic.Dictionary[int,string[]]]
    

    【讨论】:

      【解决方案3】:

      要从 Powershell v3 调用(无参数)泛型方法重载,如 OP 示例所示,请使用 @Chad Carisch 提供的参考中的脚本 Invoke-GenericMethod.ps1,@987654321 @。

      它应该看起来像

      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

      【讨论】:

        【解决方案4】:

        marsze's helpful answer 包含有关调用泛型方法的大量一般信息,但让我具体谈谈调用无参数方法的方面,如所问:

        正如问题中所暗示的:

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

        从 Windows PowerShell v5.1 / PowerShell Core v6.1.0 开始,PowerShell 具有 no 语法,允许您在这种情况下明确指定类型。。 p>

        但是,PowerShell Core 中的 a suggestion on GitHub to enhance the syntax 原则上已获得批准,但正在等待社区实施。

        目前,必须使用反射

        # 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) 上调用类型实例化方法。

        【讨论】:

          猜你喜欢
          • 2016-02-06
          • 1970-01-01
          • 2011-04-23
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-05-18
          • 1970-01-01
          相关资源
          最近更新 更多