Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I call an overloaded .NET function which uses a C# out argument from Powershell?

I know that Powershell can call .NET code, which might look like this

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

And I know that where C# has out arguments, Powershell has [ref] arguments, which might look like this:

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

And I know that in C# you can overload functions, like the SharpSvn library does for its SvnClient.Update() method:

  1. Update(ICollection(String)) - Recursively updates the specified paths to the latest (HEAD) revision
  2. Update(String) - Recursively updates the specified path to the latest (HEAD) revision
  3. Update(ICollection(String), SvnUpdateArgs) - Updates the specified paths to the specified revision
  4. Update(ICollection(String), SvnUpdateResult) - Recursively updates the specified paths to the latest (HEAD) revision
  5. Update(String, SvnUpdateArgs) - Recursively updates the specified path
  6. Update(String, SvnUpdateResult) - Recursively updates the specified path to the latest (HEAD) revision
  7. Update(ICollection(String), SvnUpdateArgs, SvnUpdateResult) - Updates the specified paths to the specified revision
  8. Update(String, SvnUpdateArgs, SvnUpdateResult) - Recursively updates the specified path to the latest (HEAD) revision

But, what if we want to put all of this together? If, say, I want to call the 6th version of Update(), the one that takes a String and an SvnUpdateResult, where the SvnUpdateResult is a C# out object? My first instinct was to try something like this:

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

OK, maybe I have to cast the arguments?

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

But that doesn't seem to work either. Other things I've tried:

  • casting $update as [SharpSvn.SvnUpdateResult][ref] - that is, reversing the order that I cast it. That results in an error which states: "[ref] can only be the final type in type conversion sequence."
  • Casting $update to SharpSvn.SvnUpdateResult before using it: $update = [SharpSvn.SvnUpdateResult]$null. This results in the same "Multiple ambiguous overloads" error I ran into above
  • Casting $update to ref before using it: $update = [ref]$null. This results in an error: 'Cannot convert the "System.Management.Automation.PSReference" value of type "System.Management.Automation.PSReference" to type "SharpSvn.SvnUpdateResult".'

It seems like casting it twice is the problem - the final cast just overrides the first cast, they don't complement each other. Is this what's happening? Is there a way to cast something twice? Is there another way around this problem?

Thanks in advance for any help.

like image 400
Micah R Ledbetter Avatar asked Dec 19 '12 18:12

Micah R Ledbetter


2 Answers

This might not be an ideal solution but you could use Add-Type and make a more PowerShell friendly type that wraps the Update method.

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)
like image 108
Adam Driscoll Avatar answered Oct 21 '22 02:10

Adam Driscoll


This does seem to be quite hard to get right.

What version of PowerShell is this? Are SvnUpdateArgs and SvnUpdateResult related (i.e. one class derives from the other)? I shall assume not.

Based on a similar scenario I constructed, with the current version (4.0) of PowerShell, this works, I think:

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

However, I could only get this to work with one of the overloads!? Not sure if it is by coincidence, but this was the overload which was listed first when I said $svnclient.Update to see the overload definitions.

In my version of PowerShell, if I give the second argument as [ref][SharpSvn.SvnUpdateResult]$update, the overload seems to be resolved correctly, the method runs without error, but the object assigned to the "out" parameter seems to be lost.

Even if $repopath is declared type-safely as [string]$repopath = "C:\path\to\localrepo", it seems that we still have to say [string]$repopath again when we pass it (by value).

like image 45
Jeppe Stig Nielsen Avatar answered Oct 21 '22 03:10

Jeppe Stig Nielsen