Как я могу найти метод, который вызвал текущий метод?
при входе в C#, как я могу узнать имя метода, который вызвал текущий метод? Я знаю все о System.Reflection.MethodBase.GetCurrentMethod()
, но я хочу пойти на один шаг ниже в стеке. Я рассматривал разбор трассировки стека, но я надеюсь найти более чистый более явный способ, что-то вроде Assembly.GetCallingAssembly()
но для методов.
17 ответов:
попробуйте это:
using System.Diagnostics; // Get call stack StackTrace stackTrace = new StackTrace(); // Get calling method name Console.WriteLine(stackTrace.GetFrame(1).GetMethod().Name);
В C# 5 Вы можете получить эту информацию с помощью caller info:
//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
мы можем немного улучшить код г-на Асада (текущий принятый ответ), создав экземпляр только кадра, который нам действительно нужен, а не весь стек:
new StackFrame(1).GetMethod().Name;
это может работать немного лучше, хотя, по всей вероятности, он все еще должен использовать полный стек для создания этого одного кадра. Кроме того, он по-прежнему имеет те же предостережения, что и Алекс Лайман (оптимизатор/собственный код может повредить результаты). Наконец, вы можете проверить, чтобы убедиться, что
new StackFrame(1)
или.GetFrame(1)
не возвращатьnull
, так же маловероятно, как эта возможность может показаться.посмотреть этот вопрос: можете ли вы использовать отражение, чтобы найти имя текущего выполняемого метода?
быстрый обзор 2 подходов с сравнением скорости является важной частью.
определение вызывающего объекта во время компиляции
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); }
сравнение 2 подходит
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" и ".Чистый стандарт".
ссылки
обратите внимание, что это будет ненадежно в коде выпуска, из-за оптимизации. Кроме того, запуск приложения в режиме песочницы (сетевой ресурс) не позволит вам захватить кадр стека вообще.
считают аспектно-ориентированное программирование (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); }
это выведет текущую дату и время, а затем "пространство имен.имя класса.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-calling-method/
другой подход, который я использовал, заключается в добавлении параметра к рассматриваемому методу. Например, вместо
void Foo()
используйтеvoid Foo(string context)
. Затем передайте некоторую уникальную строку, которая указывает на вызывающий контекст.Если вам нужен только вызывающий / контекст для разработки, вы можете удалить
param
перед отправкой.
посмотри имя метода ведения журнала в .NET. остерегайтесь использовать его в производственном коде. StackFrame может быть ненадежным...
мы также можем использовать лямбда-выражения для того, чтобы найти звонившего.
Предположим, у вас есть метод, определенный вами:
public void MethodA() { /* * Method code here */ }
и вы хотите найти его абонента.
1. Измените сигнатуру метода, чтобы у нас был параметр типа Action (Func также будет работать):
public void MethodA(Action helperAction) { /* * Method code here */ }
2. Лямбда-имена не генерируются случайным образом. Правило выглядит следующим образом: >
__X где CallerMethodName заменяется предыдущим функция и X-это индекс. private MethodInfo GetCallingMethodInfo(string funcName) { return GetType().GetMethod( funcName.Substring(1, funcName.IndexOf(">", 1, StringComparison.Ordinal) - 1) ); }
3. При вызове метода параметр Action/Func должен быть сгенерирован вызывающим методом. Пример:
MethodA(() => {});
4. Внутри MethodA теперь мы можем вызвать вспомогательную функцию, определенную выше, и найти MethodInfo вызывающего метода.
пример:
MethodInfo callingMethodInfo = GetCallingMethodInfo(serverCall.Method.Name);