Получение имени вызывающего метода из метода

У меня есть метод в объекте, который вызывается из нескольких мест внутри объекта. Есть ли быстрый и простой способ узнать имя метода, который вызвал этот популярный метод.

ПРИМЕР псевдокода:

public Main()
{
     PopularMethod();
}

public ButtonClick(object sender, EventArgs e)
{
     PopularMethod();
}

public Button2Click(object sender, EventArgs e)
{
     PopularMethod();
}

public void PopularMethod()
{
     //Get calling method name
}

Внутри PopularMethod() я хотел бы увидеть значение Main, если он был вызван из Main ... Я хотел бы увидеть ButtonClick, если PopularMethod() был вызван из ButtonClick

Я смотрел на System.Reflection.MethodBase.GetCurrentMethod(), но это не помогло мне найти метод вызова. Я посмотрел на класс StackTrace, но мне действительно не нравилось запускать всю трассировку стека каждый раз, когда вызывается этот метод.


person Community    schedule 05.03.2009    source источник


Ответы (7)


Я не думаю, что это можно сделать без отслеживания стека. Однако сделать это довольно просто:

StackTrace stackTrace = new StackTrace();
MethodBase methodBase = stackTrace.GetFrame(1).GetMethod();
Console.WriteLine(methodBase.Name); // e.g.

Однако я думаю, вам действительно стоит остановиться и спросить себя, нужно ли это.

person jason    schedule 05.03.2009
comment
Для получения дополнительной информации см .: Discussion.joelonsoftware.com/default.asp?dotnet.12.511358 .10 - person Eric Lathrop; 05.03.2009
comment
System.Diagnostics - это пространство имен StackTrace, System.Reflection - это пространство имен MethodBase. - person Guvante; 05.03.2009
comment
Джейсон - Спасибо, я не уверен, насколько ресурсоемким является метод StackTrace, но он дает мне то, что я ищу. Для меня это всего лишь отладочный блок кода, он не будет запущен в производство. Еще раз спасибо за вашу помощь ! - person ; 05.03.2009
comment
Для меня это запах кода; возможно, здесь есть некоторые проблемы с вашим объектно-ориентированным дизайном, из-за которых вы захотите узнать, кто вызвал ваш метод. В таком случае, может быть, у вас есть разрыв полиморфизма? - person jcollum; 05.03.2009
comment
@Scott Vercuski - Это чрезвычайно дорогостоящая операция, и если есть какой-то способ ее избежать, вам действительно стоит. Это хуже, чем выбрасывать исключения, и это плохо. Приятно, что это можно сделать, но нельзя злоупотреблять. - person John Leidegren; 06.03.2009
comment
@John Leidegren - Определенно ... это не входит в производственный код, мы просто проводим массивное тестирование QA и получаем некоторые странные события, это просто, чтобы помочь выяснить, где дела идут наперекосяк. Спасибо ! - person ; 06.03.2009

В .NET 4.5 / C # 5 это просто:

public void PopularMethod([CallerMemberName] string caller = null)
{
     // look at caller
}

компилятор автоматически добавляет имя вызывающего абонента; так:

void Foo() {
    PopularMethod();
}

перейдет в "Foo".

person Marc Gravell    schedule 02.01.2013
comment
Каковы последствия этого решения для производительности? - person Feckmore; 16.01.2013
comment
@Feckmore none вообще: компилятор добавляет его как литерал во время компиляции. Это будет значительно быстрее, чем что-либо вроде StackTrace и т. Д., В основном он компилируется как PopularMethod (CurrentMethodName) - person Marc Gravell; 17.01.2013
comment
Я получаю сообщение об ошибке "Не могу найти имя вызывающего абонента". VS Экспресс C # 2010 - person john ktejik; 21.02.2014
comment
@ user396483, вам необходимо настроить таргетинг на .NET 4.5 или выше, чтобы атрибут существовал (хотя вы можете определить его самостоятельно), и вам нужно использовать C # 5, чтобы он работал желаемым образом. VS Express 2013 доступен и бесплатно - person Marc Gravell; 21.02.2014

На самом деле это действительно просто.

public void PopularMethod()
{
    var currentMethod = System.Reflection.MethodInfo
        .GetCurrentMethod(); // as MethodBase
}

Но будьте осторожны, я немного скептически отношусь к тому, имеет ли встраивание метода какой-либо эффект. Вы можете сделать это, чтобы убедиться, что JIT-компилятор не будет мешать.

[System.Runtime.CompilerServices.MethodImpl(
 System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
public void PopularMethod()
{
    var currentMethod = System.Reflection.MethodInfo
        .GetCurrentMethod();
}

Чтобы получить вызывающий метод:

[System.Runtime.CompilerServices.MethodImpl(
 System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
public void PopularMethod()
{
    // 1 == skip frames, false = no file info
    var callingMethod = new System.Diagnostics.StackTrace(1, false)
         .GetFrame(0).GetMethod();
}
person John Leidegren    schedule 05.03.2009
comment
Джон Лейдгрен - Это даст мне имя PopularMethod, но мне нужно имя метода, который вызвал PopularMethod. - person ; 05.03.2009
comment
Хорошо, тогда трассировка стека - это то, что вам нужно. Я неправильно понял твой вопрос. - person John Leidegren; 05.03.2009
comment
Вместо этого просто используйте .GetFrame (1), не так ли? - person Andrew Backer; 18.02.2010
comment
+1 за MethodInlining.NoInlining. Кстати, ложь при вызове StackTrace .ctor избыточна, не так ли? - person Ruben Bartelink; 15.10.2010
comment
Я считаю, что он показал немного лучшую производительность, если бы я не запрашивал информацию о файле явно. По правде говоря, это, вероятно, не имеет значения, основная причина, по которой я так поступил, заключалась в том, что мне не нужна была информация о файле. - person John Leidegren; 15.10.2010

Просто передайте параметр

public void PopularMethod(object sender)
{

}

ИМО: Если он достаточно хорош для событий, он должен быть достаточно хорош для этого.

person Sruly    schedule 05.03.2009
comment
Sruly - Да, это определенно вариант, я пытался сделать это, не изменяя вызов метода. Это был бы мой последний вариант. - person ; 05.03.2009
comment
Если вы не представляете это как общедоступный API, зачем вам беспокоиться и использовать отражение, если вы можете просто сделать это таким образом? ПОМНИТЕ KISS - person Sruly; 05.03.2009
comment
Это ЛУЧШИЙ ответ, не используйте отражение или другие вещи, идентифицируйте вызывающего абонента с помощью параметра. - person Allen Rice; 05.03.2009
comment
Если вы ищете что-то вроде информации о пространстве имен / сборке, передача типа может быть лучшим выбором, поскольку вызов может исходить из статического метода. - person Chris Moschini; 24.01.2013
comment
Я написал код для многих систем. Часто разработчики хотят отслеживать метод, который создает запись в журнале. Если код ведения журнала требует передачи имени метода вручную, разработчик должен вводить имя метода в виде текста каждый раз, когда он вызывает метод ведения журнала. Теперь используйте тот же метод ведения журнала во многих различных частях программы и посмотрите, что происходит, когда методы добавляются, удаляются и переименовываются многими разработчиками в течение нескольких лет. Автоматизация передачи имени вызывающего метода значительно упростит разработку и отладку. - person Zarepheth; 13.12.2013

Я часто обнаруживал, что сам желаю сделать это, но всегда заканчивал рефакторингом дизайна моей системы, чтобы не получить этот антипаттерн «Хвост виляет собакой». Результатом всегда была более надежная архитектура.

person JonPen    schedule 25.07.2012

Хотя вы можете наиболее точно отследить стек и понять его таким образом, я настоятельно рекомендую вам переосмыслить свой дизайн. Если вашему методу нужно знать о каком-то «состоянии», я бы сказал, просто создайте перечисление или что-то в этом роде и возьмите это как параметр для вашего PopularMethod (). Что-то в этом роде. Исходя из того, что вы публикуете, отслеживание стека было бы излишним, IMO.

person BFree    schedule 05.03.2009

Я думаю, вам нужно использовать класс StackTrace, а затем StackFrame.GetMethod() в следующем кадре.

Хотя это кажется странным для использования Reflection. Если вы определяете PopularMethod, не можете определить параметр или что-то еще, чтобы передать информацию, которую вы действительно хотите. (Или добавьте базовый класс или что-то в этом роде ...)

person C. Dragon 76    schedule 05.03.2009