При входе на С#, как узнать имя метода, называемого текущим методом? Я знаю все о System.Reflection.MethodBase.GetCurrentMethod()
, но я хочу сделать один шаг ниже этого в трассировке стека. Я рассмотрел разбор трассировки стека, но я надеюсь найти более чистый более явный способ, например Assembly.GetCallingAssembly()
, но для методов.
Как найти метод, называемый текущим методом?
Ответ 1
Попробуйте следующее:
using System.Diagnostics;
// Get call stack
StackTrace stackTrace = new StackTrace();
// Get calling method name
Console.WriteLine(stackTrace.GetFrame(1).GetMethod().Name);
Ответ 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
. Однако есть некоторые оговорки к этому подходу:
- Он представляет собой стек времени выполнения - оптимизация может встроить метод, и вы не увидите этот метод в трассировке стека.
- Он не будет отображать какие-либо родные фреймы, поэтому, если есть шанс, что ваш метод вызывается собственным методом, это не сработает, и на самом деле нет доступного в настоящее время способа сделать это.
(ПРИМЕЧАНИЕ. Я просто расширяю ответ, предоставленный Firas Assad.)
Ответ 6
Быстрое повторение двух подходов, когда сравнение скорости является важной частью.
Определение вызывающего абонента во время компиляции
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".
Рекомендации
Ответ 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
перед отправкой.
Ответ 14
Посмотрите Имя метода ведения журнала в .NET. Остерегайтесь использовать его в производственном коде. StackFrame может быть ненадежным...
Ответ 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(">", 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;