web-dev-qa-db-fra.com

Comment puis-je trouver la méthode qui a appelé la méthode actuelle?

Lors de la connexion en C #, comment puis-je connaître le nom de la méthode qui a appelé la méthode actuelle? Je connais tout à propos de System.Reflection.MethodBase.GetCurrentMethod(), mais je souhaite franchir une étape supplémentaire dans la trace de pile. J'ai envisagé d'analyser la trace de la pile, mais j'espère trouver un moyen plus clair et plus clair, quelque chose comme Assembly.GetCallingAssembly() mais pour les méthodes.

421
flipdoubt

Essaye ça:

using System.Diagnostics;
// Get call stack
StackTrace stackTrace = new StackTrace();

// Get calling method name
Console.WriteLine(stackTrace.GetFrame(1).GetMethod().Name);

C'est à partir de Obtenir la méthode d'appel à l'aide de Reflection [C #].

427
Firas Assaad

En C # 5, vous pouvez obtenir ces informations en utilisant les informations de l'appelant:

//using System.Runtime.CompilerServices;
public void SendError(string Message, [CallerMemberName] string callerName = "") 
{ 
    Console.WriteLine(callerName + "called me."); 
} 

Vous pouvez également obtenir les [CallerFilePath] et [CallerLineNumber].

290
Coincoin

Vous pouvez utiliser les informations sur l'appelant et des paramètres facultatifs:

public static string WhoseThere([CallerMemberName] string memberName = "")
{
       return memberName;
}

Ce test illustre ceci:

[Test]
public void Should_get_name_of_calling_method()
{
    var methodName = CachingHelpers.WhoseThere();
    Assert.That(methodName, Is.EqualTo("Should_get_name_of_calling_method"));
}

Tandis que le StackTrace fonctionne assez rapidement et ne pose pas de problème de performances dans la plupart des cas, l’information de l’appelant est encore beaucoup plus rapide. Sur un échantillon de 1000 itérations, je l'ai enregistré 40 fois plus vite.

95
dove

En général, vous pouvez utiliser la classe System.Diagnostics.StackTrace pour obtenir un System.Diagnostics.StackFrame , puis utiliser la méthode GetMethod() pour obtenez un objet System.Reflection.MethodBase . Cependant, il y a quelques réserves à cette approche:

  1. Cela représente la pile d'exécution - les optimisations peuvent intégrer une méthode, et vous aurez not voir cette méthode dans la trace de la pile.
  2. pas ne montrera aucun cadre natif, donc s'il y a une chance que votre méthode soit appelée par une méthode native, cela ne fonctionnera pas , et il n'existe en fait aucun moyen de fais le.

( NOTE: je développe juste la réponse fournie par Firas Assad .)

60
Alex Lyman

Nous pouvons améliorer légèrement le code de M. Assad (la réponse actuellement acceptée) en instanciant uniquement le cadre dont nous avons réellement besoin, plutôt que la totalité de la pile:

new StackFrame(1).GetMethod().Name;

Cela pourrait être un peu plus performant, même si, selon toute vraisemblance, il doit encore utiliser toute la pile pour créer ce seul cadre. En outre, il a toujours les mêmes réserves que Alex Lyman a souligné (optimiseur/code natif pourrait corrompre les résultats). Enfin, vous pouvez vérifier que new StackFrame(1) ou .GetFrame(1) ne renvoie pas null, aussi improbable que cela puisse paraître.

Voir cette question connexe: Pouvez-vous utiliser la réflexion pour trouver le nom de la méthode en cours d’exécution?

57
Joel Coehoorn

Une récapitulation rapide des 2 approches, la comparaison de la vitesse étant l’essentiel.

http://geekswithblogs.net/BlackRabbitCoder/archive/2013/07/25/c.net-little-wonders-getting-caller-information.aspx

Détermination de l'appelant au moment de la compilation

static void Log(object message, 
[CallerMemberName] string memberName = "",
[CallerFilePath] string fileName = "",
[CallerLineNumber] int lineNumber = 0)
{
    // we'll just use a simple Console write for now    
    Console.WriteLine("{0}({1}):{2} - {3}", fileName, lineNumber, memberName, message);
}

Détermination de l'appelant à l'aide de la pile

static void Log(object message)
{
    // frame 1, true for source info
    StackFrame frame = new StackFrame(1, true);
    var method = frame.GetMethod();
    var fileName = frame.GetFileName();
    var lineNumber = frame.GetFileLineNumber();

    // we'll just use a simple Console write for now    
    Console.WriteLine("{0}({1}):{2} - {3}", fileName, lineNumber, method.Name, message);
}

Comparaison des 2 approches

Time for 1,000,000 iterations with Attributes: 196 ms
Time for 1,000,000 iterations with StackTrace: 5096 ms

Vous voyez donc que l'utilisation des attributs est beaucoup, beaucoup plus rapide! Près de 25x plus rapide en fait.

52
Tikall

A partir de .NET 4.5, vous pouvez utiliser Informations sur l'appelant Attributs:

  • CallerFilePath - Le fichier source qui a appelé la fonction;
  • CallerLineNumber - Ligne de code qui a appelé la fonction; 
  • CallerMemberName - Membre qui a appelé la fonction.

    public void WriteLine(
        [CallerFilePath] string callerFilePath = "", 
        [CallerLineNumber] long callerLineNumber = 0,
        [CallerMemberName] string callerMember= "")
    {
        Debug.WriteLine(
            "Caller File Path: {0}, Caller Line Number: {1}, Caller Member: {2}", 
            callerFilePath,
            callerLineNumber,
            callerMember);
    }
    

Cette fonctionnalité est également présente dans ".NET Core" et ".NET Standard".

Références

  1. Microsoft - Informations sur le demandeur (C #)
  2. Microsoft - CallerFilePathAttribute Class
  3. Microsoft - CallerLineNumberAttribute Class
  4. Microsoft - CallerMemberNameAttribute Class
20
Ivan Pinto

Notez que cela ne sera pas fiable dans le code de version, à cause de l'optimisation. En outre, l'exécution de l'application en mode bac à sable (partage réseau) ne vous permettra pas de saisir le cadre de la pile.

Considérez la programmation orientée aspect (AOP), comme PostSharp , qui au lieu d’être appelé à partir de votre code, modifie votre code et sait donc où il se trouve à tout moment.

/// <summary>
/// Returns the call that occurred just before the "GetCallingMethod".
/// </summary>
public static string GetCallingMethod()
{
   return GetCallingMethod("GetCallingMethod");
}

/// <summary>
/// Returns the call that occurred just before the the method specified.
/// </summary>
/// <param name="MethodAfter">The named method to see what happened just before it was called. (case sensitive)</param>
/// <returns>The method name.</returns>
public static string GetCallingMethod(string MethodAfter)
{
   string str = "";
   try
   {
      StackTrace st = new StackTrace();
      StackFrame[] frames = st.GetFrames();
      for (int i = 0; i < st.FrameCount - 1; i++)
      {
         if (frames[i].GetMethod().Name.Equals(MethodAfter))
         {
            if (!frames[i + 1].GetMethod().Name.Equals(MethodAfter)) // ignores overloaded methods.
            {
               str = frames[i + 1].GetMethod().ReflectedType.FullName + "." + frames[i + 1].GetMethod().Name;
               break;
            }
         }
      }
   }
   catch (Exception) { ; }
   return str;
}
8
Flanders

Évidemment, c'est une réponse tardive, mais j'ai une meilleure option si vous pouvez utiliser .NET 4.5 ou plus:

internal static void WriteInformation<T>(string text, [CallerMemberName]string method = "")
{
    Console.WriteLine(DateTime.Now.ToString() + " => " + typeof(T).FullName + "." + method + ": " + text);
}

Ceci imprimera la date et l'heure actuelles, suivies de "Namespace.ClassName.MethodName" et se terminant par ": text".
Exemple de sortie:

6/17/2016 12:41:49 PM => WpfApplication.MainWindow..ctor: MainWindow initialized

Exemple d'utilisation:

Logger.WriteInformation<MainWindow>("MainWindow initialized");
8
Camilo Terevinto

Peut-être que vous cherchez quelque chose comme ça:

StackFrame frame = new StackFrame(1);
frame.GetMethod().Name; //Gets the current method name

MethodBase method = frame.GetMethod();
method.DeclaringType.Name //Gets the current class name
6
jesal
private static MethodBase GetCallingMethod()
{
  return new StackFrame(2, false).GetMethod();
}

private static Type GetCallingType()
{
  return new StackFrame(2, false).GetMethod().DeclaringType;
}

Une classe fantastique est ici: http://www.csharp411.com/c-get-calling-method/

4
Tebo

Une autre approche que j'ai utilisée consiste à ajouter un paramètre à la méthode en question. Par exemple, au lieu de void Foo(), utilisez void Foo(string context). Puis transmettez une chaîne unique qui indique le contexte d’appel.

Si vous avez seulement besoin de l'appelant/du contexte pour le développement, vous pouvez supprimer la param avant l'expédition.

2
GregUzelac
StackFrame caller = (new System.Diagnostics.StackTrace()).GetFrame(1);
string methodName = caller.GetMethod().Name;

sera suffisant, je pense.

1
caner

Regardez Nom de la méthode de journalisation dans .NET. Attention à l'utiliser dans le code de production. StackFrame n'est peut-être pas fiable ...

1
Yuval Peled

Nous pouvons également utiliser le lambda pour trouver l'appelant.

Supposons que vous ayez une méthode définie par vous:

public void MethodA()
    {
        /*
         * Method code here
         */
    }

et vous voulez trouver l'appelant.

1 . Changez la signature de la méthode pour avoir un paramètre de type Action (Func fonctionnera également):

public void MethodA(Action helperAction)
        {
            /*
             * Method code here
             */
        }

2 . Les noms Lambda ne sont pas générés aléatoirement. La règle semble être:> <CallerMethodName> __ X CallerMethodName est remplacé par la fonction précédente et X est un index.

private MethodInfo GetCallingMethodInfo(string funcName)
    {
        return GetType().GetMethod(
              funcName.Substring(1,
                                funcName.IndexOf("&gt;", 1, StringComparison.Ordinal) - 1)
              );
    }

3 . Lorsque nous appelons MethodA, le paramètre Action/Func doit être généré par la méthode de l'appelant . Exemple:

MethodA(() => {});

4 . Dans MethodA, nous pouvons maintenant appeler la fonction d'assistance définie ci-dessus et trouver le MethodInfo de la méthode de l'appelant.

Exemple:

MethodInfo callingMethodInfo = GetCallingMethodInfo(serverCall.Method.Name);
1
smiron

Pour obtenir le nom de la méthode et le nom de la classe, essayez ceci:

    public static void Call()
    {
        StackTrace stackTrace = new StackTrace();

        var methodName = stackTrace.GetFrame(1).GetMethod();
        var className = methodName.DeclaringType.Name.ToString();

        Console.WriteLine(methodName.Name + "*****" + className );
    }
0
Arian

Informations supplémentaires à la réponse de Firas Assaad.

J'ai utilisé new StackFrame(1).GetMethod().Name; dans .net core 2.1 avec injection de dépendance et la méthode d'appel est appelée "Démarrer". 

J'ai essayé avec [System.Runtime.CompilerServices.CallerMemberName] string callerName = "".__ et cela me donne la méthode d'appel correcte

0
cdev