Вопрос

Как при входе в C# узнать имя метода, вызвавшего текущий метод?Я знаю все о System.Reflection.MethodBase.GetCurrentMethod(), но я хочу пойти на один шаг ниже этого в трассировке стека.Я рассматривал возможность анализа трассировки стека, но надеюсь найти более чистый и явный способ, что-то вроде Assembly.GetCallingAssembly() но для методов.

Это было полезно?

Решение

Попробуйте это:

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

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

Это из Получить метод вызова с помощью Reflection [C #] .

Другие советы

В C # 5 вы можете получить эту информацию, используя информацию о вызывающем абоненте:

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

Вы также можете получить [CallerFilePath] и [CallerLineNumber].

Вы можете использовать информацию о вызывающем абоненте и дополнительные параметры:

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

Этот тест иллюстрирует это:

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

Несмотря на то, что StackTrace работает достаточно быстро и выше, и в большинстве случаев это не будет проблемой производительности, информация о вызывающем абоненте все же намного быстрее. В выборке из 1000 итераций я работал в 40 раз быстрее.

В общем, вы можете использовать System.Diagnostics.StackTrace класс, чтобы получить System.Diagnostics.StackFrame , и затем используйте GetMethod() метод, чтобы получить System.Reflection.MethodBase объект. Однако есть некоторые предостережения для этого подхода: <Ол>

  • Он представляет время выполнения стека - оптимизации могут встроить метод, и вы не увидите этот метод в трассировке стека.
  • Он не будет показывать какие-либо собственные кадры, поэтому если даже существует вероятность вызова вашего метода собственным методом, это не будет , и на самом деле в настоящее время не существует способа сделать это.
  • ( ПРИМЕЧАНИЕ. Я просто расширяю страницу ответ предоставлен Фирасом Асадом .)

    Краткий обзор двух подходов, причем сравнение скорости является важной частью.

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

    Определение вызывающей стороны во время компиляции

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

    Определение звонящего по стеку

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

    Сравнение двух подходов

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

    Как видите, использование атрибутов намного, намного быстрее! Почти 25x   быстрее на самом деле.

    Начиная с .NET 4.5 вы можете использовать Информация о вызывающем абоненте Атрибуты:

    • CallerFilePath - Исходный файл, вызвавший функцию;
    • CallerLineNumber - Строка кода, вызывающая функцию;
    • CallerMemberName — Член, вызвавший функцию.

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

     

    Эта возможность также присутствует в «.NET Core» и «.NET Standard».

    Рекомендации

    1. Microsoft — информация о вызывающем абоненте (C#)
    2. Майкрософт - CallerFilePathAttribute Сорт
    3. Майкрософт - CallerLineNumberAttribute Сорт
    4. Майкрософт - CallerMemberNameAttribute Сорт

    Обратите внимание, что из-за оптимизации это будет ненадежным в коде выпуска. Кроме того, запуск приложения в режиме «песочницы» (общий сетевой ресурс) вообще не позволяет захватывать кадр стека.

    Рассмотрим аспектно-ориентированное программирование (AOP), например, PostSharp , который вместо вызова из вашего кода изменяет ваш код и, таким образом, всегда знает, где он находится.

    /// <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;
    }
    

    Очевидно, это поздний ответ, но у меня есть лучший вариант, если вы можете использовать .NET 4.5 или более позднюю версию:

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

    При этом будут напечатаны текущие дата и время, за которыми следует " Namespace.ClassName.MethodName " и заканчивается на " ;: text " ;.
    Пример вывода:

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

    Пример использования:

    Logger.WriteInformation<MainWindow>("MainWindow initialized");
    

    Может быть, вы ищете что-то вроде этого:

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

    Фантастический класс здесь: http://www.csharp411.com/c -get-колл-метод /

    Другой подход, который я использовал, заключается в добавлении параметра к рассматриваемому методу. Например, вместо void Foo() используйте void Foo(string context). Затем передайте некоторую уникальную строку, которая указывает на вызывающий контекст.

    Если вам нужен только вызывающий / контекст для разработки, вы можете удалить param перед отправкой.

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

    будет достаточно, я думаю.

    Посмотрите на имя метода ведения журнала в .NET . Остерегайтесь использовать его в производственном коде. StackFrame может быть ненадежным ...

    Мы также можем использовать лямбды, чтобы найти вызывающего абонента.

    Предположим, у вас есть определенный вами метод:

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

    и вы хотите найти абонента.

    1.Измените сигнатуру метода, чтобы у нас был параметр типа Action (Func также подойдет):

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

    2.Лямбда-имена не генерируются случайным образом.Кажется, правило такое:>u003CCallerMethodName> __X, где CallermethodName заменяется предыдущей функцией, а x - индекс.

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

    3.Когда мы вызываем MethodA, параметр Action/Func должен быть сгенерирован вызывающим методом.Пример:

    MethodA(() => {});
    

    4.Внутри MethodA теперь мы можем вызвать вспомогательную функцию, определенную выше, и найти MethodInfo вызывающего метода.

    Пример:

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

    Для получения имени метода и имени класса попробуйте следующее:

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

    Дополнительная информация к ответу Firas Assaad.

    Я использовал new StackFrame(1).GetMethod().Name; в ядре .net 2.1 с внедрением зависимостей, и я получаю вызывающий метод как «Пуск».

    Я пытался с [System.Runtime.CompilerServices.CallerMemberName] string callerName = "" и это дает мне правильный метод вызова

    var callingMethod = new StackFrame(1, true).GetMethod();
    string source = callingMethod.ReflectedType.FullName + ": " + callingMethod.Name;
    
    Лицензировано под: CC-BY-SA с атрибуция
    Не связан с StackOverflow
    scroll top