web-dev-qa-db-fra.com

Comment passer une fonction comme paramètre en C #?

Est-il possible de passer une fonction comme paramètre en C #? Je peux le faire en utilisant les classes Func ou Action, mais cela m'oblige à déclarer la signature de la fonction entière à la fois. Lorsque j'essaie d'utiliser Delegate, j'obtiens une erreur de compilation disant qu'il ne peut pas convertir un groupe de méthodes en Delegate.

Je travaille sur Axial et j'essaie de permettre aux utilisateurs d'appeler des services Web. Ce que je veux, c'est la possibilité de créer la classe proxy Visual Studio, puis de transmettre la fonction générée. La signature de la fonction n'a pas d'importance car le code généré utilise uniquement le nom de la fonction. Cependant, j'aimerais transmettre la fonction au lieu du nom pour deux raisons: la possibilité d'utiliser la propriété Url du proxy et une erreur de compilation si le service Web n'existe pas ou est mis à jour dans Visual Studio.


public void AlertIt(object o) {
    Axial.DOM.Window.Alert(o.ToString());
}
public void CallAddService() {
    object[] param = new object[] { int.Parse(txtA.Text), int.Parse(txtB.Text) };
    Axial.ServerScript.CallWebService(new WSProxy.WS().Add, param, AlertIt, AlertIt);
}

class Axial.ServerScript {
    public void CallWebService(Delegate method, object[] param, Action<object> successCallback, Action<object> failureCallback) {
        // translate to javascript (already working)
    }
}
55
Dan Goldstein

Je pense que ce que vous voulez c'est:

static object InvokeMethod(Delegate method, params object[] args){
    return method.DynamicInvoke(args);
}

static int Add(int a, int b){
    return a + b;
}

static void Test(){
    Console.WriteLine(InvokeMethod(new Func<int, int, int>(Add), 5, 4));
}

Imprime "9".

52
P Daddy

La conversion d'un groupe de méthodes, d'une méthode anonyme ou d'une expression lambda en délégué nécessite que le compilateur connaisse le type de délégué exact. Cependant, vous pouvez potentiellement utiliser des expressions lambda et des variables capturées pour simplifier les choses:

public void InvokeMethod(Action action)
{
    action();
}

public int Add(int a, int b)
{
    return a + b;
}

public void Test()
{    
    InvokeMethod(() => Add(2, 3));
}

Cela retarde essentiellement l'invocation de la manière normale, mais en enveloppant l'appel réel à Add dans un délégué Action simple.

Si cela ne répond pas à vos besoins, vous pouvez peut-être nous en dire un peu plus sur ce que vous essayez vraiment de réaliser.

EDIT: si ce code est généré, vous pouvez convertir en Func<...> avec les bons arguments - en supposant qu'il n'y en a pas trop. En dehors de cela, il n'y a pas de véritable moyen de simplement passer dans un groupe de méthodes. Il y a eu des appels occasionnels pour un opérateur "infoof (...)" (comme typeof mais pour les membres) qui vous donnerait un MemberInfo, mais cela n'existe pas réellement.

46
Jon Skeet

Vous devez d'abord avoir un délégué

delegate int Operation(int a, int b)

alors ça devient:

public void InvokeMethod(Operation method, object target, object param)
{
    method((int) target, (int) param);
}

Pas besoin d'appeler pour invoquer.

Comme pour dbone, je ne sais pas pourquoi vous auriez besoin d'un tableau params []. Pourriez-vous clarifier l'utilisation élargie des paramètres?

Aussi, je vais devoir corriger quelque chose dans votre question, car cela entraînera une erreur de compilation: p

4
Jon Limjap

C'est un exemple très simple, pour le programmeur qui connaît déjà (C/C++/VB.NET/Python)-style passe la fonction par pointeur/ref (avec Délégué C #):

        delegate void CALLBACK(String s);
        static void Main(string[] args)
        {

            Get("some string", testfunc);

            Util.pause();
        }

        static void Get(String s, CALLBACK x)
        {
            x(s);
        }


        static void testfunc(String s)
        {
            Console.WriteLine(s);
        }
3
dns

s'il vous plaît jeter un oeil à l'utilisation des délégués ici est un excellent exemple

Exemple de délégué

pourquoi utilisez-vous la réflexion? y aura-t-il jamais un nombre différent de param? ou savez-vous que la signature de la méthode restera constante (rappelez-vous également que C # prend en charge le mot-clé params [])

params c #

HTH

Des os

3
dbones

Regardez Functional Programming Series par Justin Etheredge. Vous devriez y trouver une solution à votre problème.

3
Jarek

Dites Si vous devez passer la méthode en tant que paramètre ainsi que la capture de la valeur de retour pour un traitement ultérieur. Ensuite, les exemples ci-dessus fonctionneront correctement. Mais disons que si vous devez passer une méthode avec un type de retour void, vous devez créer une autre version de la fonction InvokeMethod. Consultez l'exemple ci-dessous.

private static T retry<T>(Delegate method, params object[] args)
{
    for (int i = 0; i <= 3; i++)
    {
        try
        {
            return (T)method.DynamicInvoke(args);
        }
        catch (Exception ex)
        {
            if (i == 3)
            {
                logMessage(ex.Message);
            }
            Console.WriteLine("Retry count " + i);
            Thread.Sleep(10);
        }
    }
    return default(T);
}

private static void retry2(Delegate method, params object[] args)
{
    for (int i = 0; i <= 3; i++)
    {
        try
        {
            method.DynamicInvoke(args);
            break;
        }
        catch (Exception ex)
        {
            if (i == 3)
            {
                logMessage(ex.Message);
                //return default(T);
            }
            Console.WriteLine("Retry count " + i);
            Thread.Sleep(10);
        }
    }
}
static bool isSuccess = true;
static void logMessage(string msg)
{
    isSuccess = false;
    Console.WriteLine(msg);
}

static int Add(int a, int b)
{
    return a + b;
}

static void Add2(int a, int b)
{
    int c = a + b;
    Console.WriteLine(c);
}

static void Main(string[] args)
{
    int d = retry<int>(new Func<int, int, int>(Add), 6, 7.7);
    Console.Write("  " + d + "\n"+isSuccess);

    retry2(new Action<int, int>(Add2), 45, 60);

    Console.ReadKey();
}
2
Arup C

Quelque chose comme ça devrait fonctionner pour vous:

delegate int MyDelegate(int a, int b);
public int Add(int a, int b) {
    return a + b;
}
public void InvokeMethod(Delegate method, object[] param) {
    Console.WriteLine(method.DynamicInvoke(param));
}
public Form1() {       
    InitializeComponent();
    InvokeMethod(new MyDelegate(Add), new object[] { 1, 2 });
}

Bonne chance!

1
Mark Carpenter