【问题标题】:How can I call an overloaded .NET function which uses a C# out argument from Powershell?如何调用使用 Powershell 中的 C# out 参数的重载 .NET 函数?
【发布时间】:2012-12-19 18:42:47
【问题描述】:

我知道 Powershell 可以调用 .NET 代码,可能看起来像这样

PS> [Reflection.Assembly]::LoadFile(($ScriptDir + ".\SharpSvn-x64\SharpSvn.dll"))
PS> $SvnClient = New-Object SharpSvn.SvnClient

而且我知道 C# 有 out 参数,Powershell 有 [ref] 参数,可能看起来像这样:

PS> $info = $null
PS> $SvnClient.GetInfo($repo.local, ([ref]$info))

True

PS> $info

(...long output snipped...)
NodeKind           : Directory
Revision           : 16298
Uri                : http://server/path/to/remoterepo
FullPath           : C:\path\to\localrepo
(...long output snipped...)

而且我知道在 C# 中您可以重载函数,就像 SharpSvn 库对其 SvnClient.Update() method 所做的那样:

  1. Update(ICollection(String)) - 将指定路径递归更新到最新 (HEAD) 修订版
  2. Update(String) - 将指定路径递归更新到最新 (HEAD) 修订版
  3. Update(ICollection(String), SvnUpdateArgs) - 将指定路径更新到指定修订版
  4. Update(ICollection(String), SvnUpdateResult) - 将指定路径递归更新到最新 (HEAD) 修订版
  5. Update(String, SvnUpdateArgs) - 递归更新指定路径
  6. Update(String, SvnUpdateResult) - 以递归方式将指定路径更新为最新 (HEAD) 修订版
  7. Update(ICollection(String), SvnUpdateArgs, SvnUpdateResult) - 将指定路径更新到指定修订版
  8. Update(String, SvnUpdateArgs, SvnUpdateResult) - 以递归方式将指定路径更新为最新 (HEAD) 修订版

但是,如果我们想把所有这些放在一起怎么办?例如,如果我想调用Update() 的第 6 个版本,它接受一个字符串和一个 SvnUpdateResult,其中 SvnUpdateResult 是一个 C# 输出对象?我的第一直觉是尝试这样的事情:

PS> $repopath = "C:\path\to\localrepo"
PS> $update = $null
PS> $svnclient.update($repopath, [ref]$update)

Multiple ambiguous overloads found for "Update" and the argument count: "2".
At line:1 char:18
+ $svnclient.update <<<< ($repopath, [ref]$update)
    + CategoryInfo          : NotSpecified: (:) [], ParentContainsErrorRecordException
    + FullyQualifiedErrorId : MethodCountCouldNotFindBest

好的,也许我必须转换参数?

PS> $svnclient.update([string]$repopath, [ref][SharpSvn.SvnUpdateResult]$update)

Multiple ambiguous overloads found for "Update" and the argument count: "2".
At line:1 char:18
+ $svnclient.update <<<< ([string]$repopath, [ref][SharpSvn.SvnUpdateResult]$update)
    + CategoryInfo          : NotSpecified: (:) [], ParentContainsErrorRecordException
    + FullyQualifiedErrorId : MethodCountCouldNotFindBest

但这似乎也不起作用。我尝试过的其他事情:

  • $update 转换为[SharpSvn.SvnUpdateResult][ref] - 也就是说,颠倒我的转换顺序。这会导致错误提示:“[ref] 只能是类型转换序列中的最终类型。”
  • 在使用之前将$update 转换为SharpSvn.SvnUpdateResult$update = [SharpSvn.SvnUpdateResult]$null。这会导致我在上面遇到的相同的“多个不明确的重载”错误
  • 在使用之前将$update 转换为ref$update = [ref]$null。这会导致错误:“无法将“System.Management.Automation.PSReference”类型的“System.Management.Automation.PSReference”值转换为“SharpSvn.SvnUpdateResult”类型。'

似乎两次施法是个问题——最后一次施法只是覆盖了第一次施法,它们不会相互补充。这是怎么回事?有没有办法两次施放东西?有没有其他方法可以解决这个问题?

提前感谢您的帮助。

【问题讨论】:

  • 我发现您可以使用 $svnclient.Update.OverloadedDefinitions 检查重载的定义,但我无法从那里调用它们。
  • 深入了解 PowerShell 的类型转换/成员解析内部工作的一种方法是运行 Trace-Command -Name All -Expression {$svnclient.update($repopath, [ref]$update)} -PSHost 并查看是否有任何有用的信息。
  • 不幸的是,这会返回相同的“多重模糊重载”消息。
  • 我为这个问题创建了一个简单的重现,并为 PowerShell 创建了filed an issue

标签: .net powershell reference overloading


【解决方案1】:

这可能不是一个理想的解决方案,但您可以使用 Add-Type 并创建一个更 PowerShell 友好的类型来包装 Update 方法。

Add-Type -TypeDefinition "
public class SvnClientEx
{
   public static SvnUpdateResult Update(SvnClient client, string path)
   {
       SvnUpdateResult result; 
       client.Update(path, out result);
       return result; 
   } 
}
"

$result = [SvnClient]::Update($svnClient, $repopath)

【讨论】:

  • 这会导致这样的错误:Add-Type : c:\Users\autotester\AppData\Local\Temp\89jqcyvc.0.cs(4) : The type or namespace name 'SvnClient' could not be found (are you missing a using directive or an assembly reference?)... 我尝试在定义的顶部添加using SharpSvn;,但它给出了一个关于无法找到的类似错误SharpSvn。我确定我遗漏了一些简单的东西......我对 C# 不太熟悉,也不熟悉 Powershell 与它的交互方式。我究竟做错了什么? (感谢您的指点!)
  • 看看 Add-Type 的 ReferencedAssemblies 参数。您需要将 SVN 客户端程序集添加到上面的 Add-Type 命令中。很抱歉!
【解决方案2】:

这似乎很难做到。

这是什么版本的 PowerShell? SvnUpdateArgsSvnUpdateResult 是否相关(即一个类派生自另一个类)?我假设不会。

基于我构建的类似场景,使用当前版本(4.0)的 PowerShell,我认为这是可行的:

PS> $repopath = "C:\path\to\localrepo"
PS> [SharpSvn.SvnUpdateResult]$update = $null
PS> $svnclient.Update([string]$repopath, [ref]$update)

但是,我只能让它与其中一个重载一起工作!? 不确定是不是巧合,但这是我说$svnclient.Update 时首先列出的重载请参阅重载定义。

在我的 PowerShell 版本中,如果我将第二个参数指定为 [ref][SharpSvn.SvnUpdateResult]$update,则似乎可以正确解决重载,该方法运行时没有错误,但分配给“out”参数的对象似乎丢失了。

即使$repopath 被类型安全地声明为[string]$repopath = "C:\path\to\localrepo",我们似乎仍然需要在传递它时再次说[string]$repopath(按值)。

【讨论】:

    【解决方案3】:

    当您使用类的 out 参数调用的方法和具有类似签名的另一个重载(该类可以是此签名中的任何类)时,PowerShell 中似乎存在问题。

    我为这个问题创建了一个简单的重现,并为 PowerShell 创建了filed an issue

    【讨论】:

      【解决方案4】:

      您可以简单地使用代码块并访问此块中的结果(输出)对象:

      PS> $repopath = "C:\path\to\localrepo"
      PS> $svnclient.update($repopath, { 
            Write-Host $_.Revision
            Write-Host $_.HasRevision
            # Access all the Member and Methods of the result object here
          })
      

      【讨论】:

        【解决方案5】:

        你可以做几件事:

        首先,$svnclient.update 返回的结果是PSMethodPSMethod 有一个 Invoke 方法。你可以试试看。

        其次,如果这不起作用,您可以使用真正的反射:

        # To get all methods:
        $svnclient.gettype().getmethods()     
        
        # Then filter the results to get overload you need.
        # Use GetParameters to get parameters of an overloaded method 
        # return by GetMethods
        # For example (using PS 3.0 features):
        
        $svnclient.gettype().getmethods()|? name -eq Update |
        % {write-host "***`n"';$_} | % GetParameters
        
        # Will list parameter sets for each overload of Update, each set separated by stars
        
        # Alternatively use getmethod with appropriate parameters    
        # GetMethod (link below) can be used to select correct overload without the 
        # filtering above. The next to the last overload of GetMethod at the link is the one to use
        
        # After obtaining correct overload, then call the Invoke method like this:
        $correctOverload.Invoke( $svnclient, @($repopath, [ref] $SvnUpdateResult))
        

        GetMethod Link

        【讨论】:

        • @micah-r-ledbetter 编写 C# CmdLets 或此反射解决方案似乎是最佳选择。我将与 PowerShell 团队一起跟进为什么调用不起作用,但我怀疑修复程序很快就会出现在 PowerShell 中。我在上述解决方案中发现了问题(调用语法错误。)如果您对修复上述工作感兴趣,请回复。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2015-04-21
        • 2010-09-16
        • 2018-08-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多