Подтвердить что ты не робот

Как найти метод, называемый текущим методом?

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

4b9b3361

Ответ 2

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

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

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

Ответ 3

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

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 раз быстрее.

Ответ 4

Мы можем немного улучшить код г-на Ассада (текущий принятый ответ), создав экземпляр только нужного кадра, а не всего пакета:

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

Это может быть немного лучше, хотя, по всей вероятности, ему все равно придется использовать полный стек для создания одного кадра. Кроме того, у него все еще есть те же предостережения, которые указал Алекс Лайман (оптимизатор/собственный код может испортить результаты). Наконец, вы можете проверить, чтобы убедиться, что new StackFrame(1) или .GetFrame(1) не возвращают null, что маловероятно, поскольку эта возможность может показаться.

Смотрите этот вопрос: Можете ли вы использовать отражение, чтобы найти имя текущего исполняемого метода?

Ответ 5

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

  1. Он представляет собой стек времени выполнения - оптимизация может встроить метод, и вы не увидите этот метод в трассировке стека.
  2. Он не будет отображать какие-либо родные фреймы, поэтому, если есть шанс, что ваш метод вызывается собственным методом, это не сработает, и на самом деле нет доступного в настоящее время способа сделать это.

(ПРИМЕЧАНИЕ. Я просто расширяю ответ, предоставленный Firas Assad.)

Ответ 6

Быстрое повторение двух подходов, когда сравнение скорости является важной частью.

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 быстрее.

Ответ 7

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

  • CallerFilePath - исходный файл, который CallerFilePath функцию;
  • CallerLineNumber - строка кода, 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 - информация о вызывающем абоненте (С#)
  2. Microsoft - класс CallerFilePathAttribute
  3. Microsoft - класс CallerLineNumberAttribute
  4. Microsoft - класс CallerMemberNameAttribute

Ответ 8

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

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

Ответ 9

Очевидно, что это поздний ответ, но у меня есть лучший вариант, если вы можете использовать .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");

Ответ 10

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

Ответ 11

Возможно, вы ищете что-то вроде этого:

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

Ответ 12

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-calling-method/

Ответ 13

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

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

Ответ 15

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

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

Ответ 16

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

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

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

и вы хотите найти его вызывающим.

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

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

2. Имена Лямбды не генерируются случайным образом. Правило выглядит следующим образом: > <CallerMethodName> __ 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);

Ответ 17

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

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

Ответ 18

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

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

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

Ответ 19

var callingMethod = new StackFrame(1, true).GetMethod();
string source = callingMethod.ReflectedType.FullName + ": " + callingMethod.Name;