Como posso encontrar o método que chamou de método atual?
-
05-07-2019 - |
Pergunta
Ao fazer login no C#, como posso aprender o nome do método que chamou de método atual? Eu sei tudo sobre System.Reflection.MethodBase.GetCurrentMethod()
, mas eu quero dar um passo abaixo disso no rastreamento da pilha. Eu considerei analisar o rastreamento da pilha, mas espero encontrar uma maneira mais limpa mais explícita, algo como Assembly.GetCallingAssembly()
mas para métodos.
Solução
Experimente isso:
using System.Diagnostics;
// Get call stack
StackTrace stackTrace = new StackTrace();
// Get calling method name
Console.WriteLine(stackTrace.GetFrame(1).GetMethod().Name);
Outras dicas
Em C# 5, você pode obter essas informações usando as informações do chamador:
//using System.Runtime.CompilerServices;
public void SendError(string Message, [CallerMemberName] string callerName = "")
{
Console.WriteLine(callerName + "called me.");
}
Você também pode obter o [CallerFilePath]
e [CallerLineNumber]
.
Você pode usar as informações do chamador e os parâmetros opcionais:
public static string WhoseThere([CallerMemberName] string memberName = "")
{
return memberName;
}
Este teste ilustra isso:
[Test]
public void Should_get_name_of_calling_method()
{
var methodName = CachingHelpers.WhoseThere();
Assert.That(methodName, Is.EqualTo("Should_get_name_of_calling_method"));
}
Embora o Stacktrace funcione muito rápido acima e não seria um problema de desempenho na maioria dos casos, as informações do chamador ainda são muito mais rápidas. Em uma amostra de 1000 iterações, eu o registrei 40 vezes mais rápido.
Podemos melhorar o código do Sr. Assad (a resposta aceita atual) apenas um pouco instanciando apenas o quadro de que realmente precisamos, em vez de toda a pilha:
new StackFrame(1).GetMethod().Name;
Isso pode ter um desempenho um pouco melhor, embora com toda a probabilidade ainda precise usar a pilha completa para criar esse quadro único. Além disso, ele ainda tem as mesmas advertências que Alex Lyman apontou (o Optimizer/Native Code pode corromper os resultados). Finalmente, você pode querer verificar se tem certeza de que new StackFrame(1)
ou .GetFrame(1)
Não volte null
, por mais improvável que essa possibilidade possa parecer.
Veja esta pergunta relacionada:Você pode usar a reflexão para encontrar o nome do método de execução atualmente?
Em geral, você pode usar o System.Diagnostics.StackTrace
classe para conseguir um System.Diagnostics.StackFrame
, e então use o GetMethod()
Método para obter um System.Reflection.MethodBase
objeto. No entanto, existem algumas advertências Para esta abordagem:
- Representa o tempo de execução Stack - otimizações podem incluir um método, e você vai não Veja esse método no rastreamento da pilha.
- Será não mostre quadros nativos, por isso, se houver uma chance de seu método estar sendo chamado por um método nativo, isso irá não trabalho, e não há uma maneira atualmente disponível para fazê-lo.
(Nota: Estou apenas expandindo a resposta Fornecido por Firas Assad.)
Uma rápida recapitulação das duas abordagens, com a comparação de velocidade sendo a parte importante.
Determinando o chamador em tempo de compilação
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);
}
Determinando o chamador usando a pilha
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);
}
Comparação das 2 abordagens
Time for 1,000,000 iterations with Attributes: 196 ms
Time for 1,000,000 iterations with StackTrace: 5096 ms
Então você vê, usar os atributos é muito, muito mais rápido! Quase 25x mais rápido de fato.
A partir do .NET 4.5, você pode usar Informações do chamador Atributos:
CallerFilePath
- o arquivo de origem que chamava de função;CallerLineNumber
- linha de código que chamava de função;CallerMemberName
- Membro que chamou a função.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); }
Essa instalação também está presente em ".NET Core" e ".Net Standard".
Referências
Observe que isso não será confiável no código de liberação, devido à otimização. Além disso, a execução do aplicativo no modo Sandbox (compartilhamento de rede) não permite que você pegue o quadro da pilha.
Considerar Programação Orientada a Aspectos (AOP), como PostSharp, que, em vez de ser chamado do seu código, modifica seu código e, portanto, sabe onde está o tempo todo.
/// <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;
}
Obviamente, esta é uma resposta tardia, mas tenho uma opção melhor se você pode usar .NET 4.5 ou mais:
internal static void WriteInformation<T>(string text, [CallerMemberName]string method = "")
{
Console.WriteLine(DateTime.Now.ToString() + " => " + typeof(T).FullName + "." + method + ": " + text);
}
Isso imprimirá a data e hora atuais, seguidas por "Namespace.classname.methodname" e terminando com ": texto".
Saída de amostra:
6/17/2016 12:41:49 PM => WpfApplication.MainWindow..ctor: MainWindow initialized
Uso de amostra:
Logger.WriteInformation<MainWindow>("MainWindow initialized");
Talvez você esteja procurando por algo assim:
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;
}
Uma aula fantástica está aqui: http://www.csharp411.com/c-get-calling-method/
Outra abordagem que usei é adicionar um parâmetro ao método em questão. Por exemplo, em vez de void Foo()
, usar void Foo(string context)
. Em seguida, passe em alguma string exclusiva que indica o contexto de chamada.
Se você precisar apenas do chamador/contexto para desenvolvimento, poderá remover o param
antes do envio.
StackFrame caller = (new System.Diagnostics.StackTrace()).GetFrame(1);
string methodName = caller.GetMethod().Name;
será suficiente, eu acho.
Dar uma olhada em Nome do método de log em .NET. Cuidado ao usá -lo no código de produção. Stackframe pode não ser confiável ...
Também podemos usar o Lambda's para encontrar o chamador.
Suponha que você tenha um método definido por você:
public void MethodA()
{
/*
* Method code here
*/
}
E você quer encontrar seu chamador.
1. Altere a assinatura do método para que tenhamos um parâmetro da ação do tipo (o FUNC também funcionará):
public void MethodA(Action helperAction)
{
/*
* Method code here
*/
}
2. Os nomes de lambda não são gerados aleatoriamente. A regra parece ser:>u003CCallerMethodName> __X onde callermethodname é substituído pela função anterior e x é um índice.
private MethodInfo GetCallingMethodInfo(string funcName)
{
return GetType().GetMethod(
funcName.Substring(1,
funcName.IndexOf(">", 1, StringComparison.Ordinal) - 1)
);
}
3. Quando chamamos o MethodA, o parâmetro Ação/Func deve ser gerado pelo método do chamador. Exemplo:
MethodA(() => {});
4. Inside Methoda, agora podemos chamar a função auxiliar definida acima e encontrar o método do método de chamador.
Exemplo:
MethodInfo callingMethodInfo = GetCallingMethodInfo(serverCall.Method.Name);
Para obter o nome do método e o nome da classe, tente isto:
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 );
}
Informações extras para o Firas ASSAAD Responder.
Eu tenho usado new StackFrame(1).GetMethod().Name;
no .NET Core 2.1 com injeção de dependência e estou recebendo o método de chamada como 'start'.
Eu tentei com [System.Runtime.CompilerServices.CallerMemberName] string callerName = ""
E isso me dá um método de chamada correto
var callingMethod = new StackFrame(1, true).GetMethod();
string source = callingMethod.ReflectedType.FullName + ": " + callingMethod.Name;