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.
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 #].
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]
.
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.
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:
( NOTE: je développe juste la réponse fournie par Firas Assad .)
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?
Une récapitulation rapide des 2 approches, la comparaison de la vitesse étant l’essentiel.
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.
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
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;
}
É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");
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
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/
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.
StackFrame caller = (new System.Diagnostics.StackTrace()).GetFrame(1);
string methodName = caller.GetMethod().Name;
sera suffisant, je pense.
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 ...
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(">", 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);
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 );
}
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