Как найти метод, вызвавший текущий метод?
-
05-07-2019 - |
Вопрос
Как при входе в 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 раз быстрее.
Мы можем немного улучшить код г-на Асада (текущий принятый ответ), создав только тот кадр, который нам нужен, а не весь стек:
new StackFrame(1).GetMethod().Name;
Это может показаться немного лучше, хотя, по всей вероятности, ему все равно придется использовать полный стек для создания этого отдельного кадра. Кроме того, он по-прежнему имеет те же предостережения, на которые указал Алекс Лайман (оптимизатор / собственный код может испортить результаты). Наконец, вы можете проверить, чтобы new StackFrame(1)
или .GetFrame(1)
не возвращать null
, как маловероятно, что такая возможность может показаться.
Посмотрите этот связанный вопрос: Можете ли вы использовать отражение, чтобы найти имя выполняемого в данный момент метода?
В общем, вы можете использовать System.Diagnostics.StackTrace
класс, чтобы получить System.Diagnostics.StackFrame
, и затем используйте GetMethod()
метод, чтобы получить System.Reflection.MethodBase
объект. Однако есть некоторые предостережения для этого подхода: р>
<Ол>
( ПРИМЕЧАНИЕ. Я просто расширяю страницу ответ предоставлен Фирасом Асадом .)
Краткий обзор двух подходов, причем сравнение скорости является важной частью.
Определение вызывающей стороны во время компиляции
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».
Рекомендации
Обратите внимание, что из-за оптимизации это будет ненадежным в коде выпуска. Кроме того, запуск приложения в режиме «песочницы» (общий сетевой ресурс) вообще не позволяет захватывать кадр стека.
Рассмотрим аспектно-ориентированное программирование (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(">", 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;