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.

Foi útil?

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);

É de Obtenha o método de chamada usando a reflexão [c#.

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:

  1. 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.
  2. 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.

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

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

  1. Microsoft - Informações sobre chamadas (C#)
  2. Microsoft - CallerFilePathAttribute Classe
  3. Microsoft - CallerLineNumberAttribute Classe
  4. Microsoft - CallerMemberNameAttribute Classe

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("&gt;", 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;
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top