web-dev-qa-db-fra.com

Méthode Pass en tant que paramètre à l'aide de C #

J'ai plusieurs méthodes, toutes avec la même signature (paramètres et valeurs de retour), mais des noms différents et les éléments internes des méthodes sont différents. Je veux passer le nom de la méthode à exécuter à une autre méthode qui invoquera la méthode passée.

public int Method1(string)
{
    ... do something
    return myInt;
}

public int Method2(string)
{
    ... do something different
    return myInt;
}

public bool RunTheMethod([Method Name passed in here] myMethodName)
{
    ... do stuff
    int i = myMethodName("My String");
    ... do more stuff
    return true;
}

public bool Test()
{
    return RunTheMethod(Method1);
}

Ce code ne fonctionne pas mais c’est ce que j’essaie de faire. Ce que je ne comprends pas, c'est comment écrire le code RunTheMethod car je dois définir le paramètre.

625
user31673

Vous pouvez utiliser le délégué Func dans .net 3.5 comme paramètre dans votre méthode RunTheMethod. Le délégué Func vous permet de spécifier une méthode qui prend un certain nombre de paramètres d'un type spécifique et renvoie un seul argument d'un type spécifique. Voici un exemple qui devrait fonctionner:

public class Class1
{
    public int Method1(string input)
    {
        //... do something
        return 0;
    }

    public int Method2(string input)
    {
        //... do something different
        return 1;
    }

    public bool RunTheMethod(Func<string, int> myMethodName)
    {
        //... do stuff
        int i = myMethodName("My String");
        //... do more stuff
        return true;
    }

    public bool Test()
    {
        return RunTheMethod(Method1);
    }
}
777
Egil Hansen

Vous devez utiliser un délégué . Dans ce cas, toutes vos méthodes prennent un paramètre string et renvoient un int - il est plus simplement représenté par le délégué _Func<string, int>_.1. Donc, votre code peut devenir correct avec un changement aussi simple que celui-ci:

_public bool RunTheMethod(Func<string, int> myMethodName)
{
    // ... do stuff
    int i = myMethodName("My String");
    // ... do more stuff
    return true;
}
_

Les délégués ont certes beaucoup plus de pouvoir que cela. Par exemple, avec C #, vous pouvez créer un délégué à partir d'une expression lambda . Vous pouvez donc appeler votre méthode de la manière suivante:

_RunTheMethod(x => x.Length);
_

Cela va créer une fonction anonyme comme celle-ci:

_// The <> in the name make it "unspeakable" - you can't refer to this method directly
// in your own code.
private static int <>_HiddenMethod_<>(string x)
{
    return x.Length;
}
_

puis passez ce délégué à la méthode RunTheMethod.

Vous pouvez utiliser les délégués pour les abonnements aux événements, l'exécution asynchrone, les rappels, etc. Il vaut la peine de les lire, en particulier si vous voulez utiliser LINQ. J'ai un article qui est principalement sur les différences entre les délégués et les événements, mais vous pouvez le trouver utile quand même.


1 Ceci est basé uniquement sur le type de délégué générique Func<T, TResult> dans le cadre; vous pouvez facilement déclarer le vôtre:

_public delegate int MyDelegateType(string value)
_

puis faites le paramètre être de type MyDelegateType à la place.

343
Jon Skeet

De l'exemple d'OP:

 public static int Method1(string mystring)
 {
      return 1;
 }

 public static int Method2(string mystring)
 {
     return 2;
 }

Vous pouvez essayer Action Delegate! Et appelez ensuite votre méthode en utilisant

 public bool RunTheMethod(Action myMethodName)
 {
      myMethodName();   // note: the return value got discarded
      return true;
 }

RunTheMethod(() => Method1("MyString1"));

Ou

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

Ensuite, il suffit d'appeler la méthode

Console.WriteLine(InvokeMethod(new Func<string,int>(Method1), "MyString1"));

Console.WriteLine(InvokeMethod(new Func<string, int>(Method2), "MyString2"));
101
Zain Ali
public static T Runner<T>(Func<T> funcToRun)
{
    //Do stuff before running function as normal
    return funcToRun();
}

Usage:

var ReturnValue = Runner(() => GetUser(99));
29
kravits88

Vous devez utiliser un délégué Func<string, int>, qui représente une fonction prenant un argument string et renvoyant un int:

public bool RunTheMethod(Func<string, int> myMethod) {
    // do stuff
    myMethod.Invoke("My String");
    // do stuff
    return true;
}

Alors utilisez-le:

public bool Test() {
    return RunTheMethod(Method1);
}
11
Bruno Reis

Pour partager une solution aussi complète que possible, je vais vous présenter trois façons différentes de procéder, mais je vais maintenant commencer par le principe de base.


Courte introduction

Tous les langages CLR ( Common Language Runtime ) (tels que C # et Visual Basic) fonctionnent sous un VM appelé CLI ( Common Language Interpreter ) qui exécute le code à un niveau plus élevé que les langages natifs tels que C et C++ (qui sont directement compilés en code machine). Il s’ensuit que les méthodes ne sont pas un bloc compilé, mais qu’elles ne sont que des éléments structurés que le CLR reconnaît et utilise pour extraire son corps et le restituer dans les instructions en ligne du code machine. Ainsi, vous ne pouvez pas penser à passer une méthode en tant que paramètre, car une méthode ne produit aucune valeur par elle-même: ce n'est pas une expression valide! Donc, vous allez trébucher sur le concept de délégué.


Qu'est-ce qu'un délégué?

Un délégué représente un pointeur sur une méthode. Comme (comme je l'ai dit plus haut) une méthode n'est pas une valeur, il existe une classe spéciale dans les langages CLR: Delegate. Cette classe enveloppe n'importe quelle méthode et vous pouvez implicitement y lancer n'importe quelle méthode.

Regardez l'exemple d'utilisation suivant:

static void MyMethod()
{
    Console.WriteLine("I was called by the Delegate special class!");
}

static void CallAnyMethod(Delegate yourMethod)
{
    yourMethod.DynamicInvoke(new object[] { /*Array of arguments to pass*/ });
}

static void Main()
{
    CallAnyMethod(MyMethod);
}

Les trois manières:

  • Voie 1
    Utilisez la classe spéciale Delegate directement dans l'exemple ci-dessus. Le problème de cette solution est que votre code ne sera pas coché lorsque vous passerez vos arguments de manière dynamique sans les limiter aux types de ceux de la déclaration de méthode.

  • Voie 2/ Outre la classe spéciale Delegate, le concept des délégués s'étend aux délégués personnalisés, qui sont des déclarations de méthodes précédées du mot clé delegate et se comportent comme une méthode normale. . Ils sont vérifiés et vous obtiendrez un code " parfait ".

Regardez l'exemple suivant:

delegate void PrintDelegate(string Prompt);

static void PrintSomewhere(PrintDelegate print, string Prompt)
{
    print(Prompt);
}

static void PrintOnConsole(string Prompt)
{
    Console.WriteLine(Prompt);
}

static void PrintOnScreen(string Prompt)
{
    MessageBox.Show(Prompt);
}

static void Main()
{
    PrintSomewhere(PrintOnConsole, "Press a key to get a message");
    Console.Read();
    PrintSomewhere(PrintOnScreen, "Hello world");
}

Une deuxième option, de cette manière, pour ne pas écrire votre propre délégué personnalisé consiste à utiliser l’un d’eux déclaré dans les bibliothèques système:

  • Action encapsule un void sans argument.
  • Action<T1> encapsule un void avec un argument.
  • Action<T1, T2> encapsule une void avec deux arguments.
  • Etc...
  • Func<TR> encapsule une fonction avec le type de retour TR et sans argument.
  • Func<T1, TR> encapsule une fonction avec le type de retour TR et avec un argument.
  • Func<T1, T2, TR> encapsule une fonction avec le type de retour TR et avec deux arguments.
  • Etc...

(Cette dernière solution est que beaucoup de personnes ont posté.)

7
Davide Cannizzo

Si vous souhaitez pouvoir modifier la méthode appelée au moment de l'exécution, je vous recommande d'utiliser un délégué: http://www.codeproject.com/KB/cs/delegates_step1.aspx

Cela vous permettra de créer un objet pour stocker la méthode à appeler et vous pourrez le transmettre à vos autres méthodes en cas de besoin.

6
MadcapLaugher

Bien que la réponse acceptée soit tout à fait correcte, je voudrais proposer une méthode supplémentaire.

Je me suis retrouvé ici après avoir moi-même cherché une solution à une question similaire. Je construis un framework piloté par plugin, et dans ce cadre, je voulais que les gens puissent ajouter des éléments de menu au menu des applications à une liste générique sans exposer un objet Menu réel, car le framework peut se déployer sur d'autres plates-formes que ne pas avoir d'objets Menu UI. Ajouter des informations générales sur le menu est assez simple, mais laisser au développeur du plug-in assez de liberté pour créer le rappel, car il était difficile de cliquer sur le menu. Jusqu'à ce que je réalise que j'essayais de réinventer la roue et que les menus normaux appellent et déclenchent le rappel des événements!

Donc, la solution, aussi simple que cela puisse paraître une fois que vous vous en êtes rendu compte, m'échappait jusqu'à présent.

Créez simplement des classes séparées pour chacune de vos méthodes actuelles, héritées d'une base si vous le souhaitez, et ajoutez simplement un gestionnaire d'événements à chacune d'elles.

2
Wobbles

Voici un exemple qui peut vous aider à mieux comprendre comment passer une fonction en tant que paramètre.

Supposons que vous ayez la page Parent et que vous souhaitiez ouvrir une fenêtre contextuelle enfant. Dans la page parent, il y a une zone de texte qui doit être remplie en se basant sur la zone de texte contextuelle enfant.

Ici, vous devez créer un délégué.

Parent.cs // déclaration des délégués délégué public void FillName (String FirstName);

Maintenant, créez une fonction qui remplira votre zone de texte et la fonction devrait mapper les délégués

//parameters
public void Getname(String ThisName)
{
     txtname.Text=ThisName;
}

Maintenant, en cliquant sur le bouton, vous devez ouvrir une fenêtre contextuelle Enfant.

  private void button1_Click(object sender, RoutedEventArgs e)
  {
        ChildPopUp p = new ChildPopUp (Getname) //pass function name in its constructor

         p.Show();

    }

Dans le constructeur ChildPopUp, vous devez créer le paramètre 'type de délégué' du parent // page

ChildPopUp.cs

    public  Parent.FillName obj;
    public PopUp(Parent.FillName objTMP)//parameter as deligate type
    {
        obj = objTMP;
        InitializeComponent();
    }



   private void OKButton_Click(object sender, RoutedEventArgs e)
    {


        obj(txtFirstName.Text); 
        // Getname() function will call automatically here
        this.DialogResult = true;
    }

Voici un exemple sans paramètre: http://en.csharp-online.net/CSharp_FAQ:_How_call_a_method_using_a_name_string

avec params: http://www.daniweb.com/forums/thread98148.html#

vous passez essentiellement dans un tableau d'objets avec le nom de la méthode. vous utilisez ensuite les deux avec la méthode Invoke.

params Object [] paramètres

0
Jeremy Samuel