Provide a .NET method as a delegate callback

2019-01-23 04:09发布

问题:

What is the syntax to pass .NET method as a delegate callback to a .NET object in PowerShell.

For example:

C#:

public class Class1
{
    public static void MyMethod(Action<object> obj)
    {
         obj("Hey!");
    }
}

public class Class2
{
    public static void Callback(object obj)
    {
         Console.Writeline(obj.ToString());
    }
}

PowerShell:

[Class1]::MyMethod([Class2]::Callback)

This doesn't work.

回答1:

Working code via Adam's and Oisin's chat.

Add-Type -Language CSharpVersion3 -TypeDefinition @"
using System;

public class Class1
{
    public static void MyMethod(Action<object> obj)
    {
         obj("Hey!");
    }
}

public class Class2
{
    public static void Callback(object obj)
    {
         Console.WriteLine(obj.ToString());
    }
}
"@

$method   = [Class2].GetMethod("Callback") 
$delegate = [System.Delegate]::CreateDelegate([System.Action[Object]], $null, $method)

[Class1]::MyMethod($delegate)


回答2:

$code = @'
using System;
public class Class1
{    
    public static void MyMethod(Action<object> obj)    
    {
        obj("Hey!");    
    }
}
public class Class2
{    
    public static void Callback(object obj)    
    {         
        Console.WriteLine(obj.ToString());    
    }
}
'@

Add-Type -TypeDefinition $code -Language CSharpVersion3

[class1]::mymethod([system.action]::CreateDelegate('System.Action[Object]', [class2].getmethod('Callback')))


回答3:

Via @oising on Twitter:

@adamdriscoll you're spoiled by the c# compiler's delegate inferencing. You need to new up that action explicitly, old school styley.

$method = [Class2].GetMethod("Callback") 
$delegate = [System.Delegate]::CreateDelegate([System.Action[Object]], $null, $method
[Class1]::MyMethod($delegate)


回答4:

The type of [Class2]::Callback is System.Management.Automation.PSMethod which apparently cannot be converted by the core to the required delegate.

I am not sure that this is the best way of solving the task (I have not seen any kind of official documentation about this) but the code below works for me in this example case and other similar cases in my practice:

[Class1]::MyMethod({ [Class2]::Callback($args[0]) })

The core is able to convert our script block { [Class2]::Callback($args[0]) } to the required delegate.

P.S. Though not directly related to the question but here is yet another working example of this technique: using script blocks as match evaluator delegates in regular expressions: How to sort by file name the same way Windows Explorer does?



回答5:

I am not an expert at C#, but after reading a couple articles it seems that you are trying to use generic delegates. Your Callback method is not a generic delegate, it isn't even a delegate.

I think this is what you need to do:

C#

public class Class1
{
    public static void MyMethod(Action<object> obj)
    {
         obj("Hey!");
    }
}

public class Class2
{
    public Action<object> CallBack = obj => Console.WriteLine(obj.ToString());
}

Powershell:

[Class1]::MyMethod([Class2]::Callback)