Получить имя текущего метода из async-функции?

Есть ли способ получить имя текущего метода внутри функции async?

Я пробовал:

System.Reflection.MethodInfo.GetCurrentMethod();

И я попытался использовать StackTrace и StrackFrame следующим образом:

StackTrace strackTrace = new StackTrace();

for (int i = 0; i < strackTrace.GetFrames().Length; i++)
{
    SafeNativeMethods.EtwTraceInfo("Function" + i + ":" + function);
    SafeNativeMethods.EtwTraceInfo("Type" + i + ":" + strackTrace.GetFrame(i).GetType().Name);
    SafeNativeMethods.EtwTraceInfo("Method" + i + ":" + strackTrace.GetFrame(i).GetMethod().Name);
    SafeNativeMethods.EtwTraceInfo("ToString Call" + i + ":" + strackTrace.GetFrame(i).ToString());
}

Но ни один из них не работает, я получаю ".ctor", "InvokeMethod", "Invoke", "CreateInstance", "CreateKnownObject" или "CreateUnknownObject" или "MoveNext"

Любые идеи о том, как я могу это сделать? Я хочу создать общую функцию журнала, и я не хочу передавать имя функции, которая вызвала функцию журнала, поэтому я попробовал метод stacktrace, не работал.

Я отказался от этого и сказал: "Хорошо, я передам имя функции в качестве первого параметра, но когда я вызвал метод отражения от вызывающей функции, вызывающей общую функцию регистрации, я всегда получаю".ctor "

Любые идеи? Обратите внимание, что общая функция logger, которую я вызываю, является статическим методом в том же классе (это должно быть так на данный момент...).

Ответы

Ответ 1

С# 5 добавлены атрибуты информации о вызывающем абоненте, которые могут дать вам больше того, что вы ищете. Обратите внимание, что они вставляют соответствующую информацию в сайт вызова во время компиляции, а не используют информацию о времени выполнения. Функциональность более ограничена (вы не можете получить полный стек вызовов, очевидно), но это намного быстрее.

Пример использования CallerMemberNameAttribute:

using System.Runtime.CompilerServices;

public static void Main(string[] args)
{
    Test().Wait();            
}

private static async Task Test()
{
    await Task.Yield();
    Log();
    await Task.Yield();
}

private static void Log([CallerMemberName]string name = "")
{
    Console.WriteLine("Log: {0}", name);
}

Существуют также атрибуты CallerFilePath и CallerLineNumber, которые могут получать другие фрагменты информации о сайте вызова.

Ответ 2

Вместо того, чтобы делать ручные кадровые переходы, которые являются дорогостоящими и рискованными (поскольку версии Release могут оптимизировать некоторые методы), вы можете использовать CallerMemberNameAttribute, один из атрибутов Caller Information, который был добавлен в .NET 4.5 (который вы уже используете, если вы используете async/wait ) для этого точного сценария - передача имени члена для регистраторов, обработчиков свойств и т.п.

Это происходит следующим образом:

public void LogMessage(string message, [CallerMemberName] string caller = "")
{ 
    // caller should contain the name of the method that called LogMessage.
}

Я не знаю никаких ограничений, которые имеют методы async.

Ответ 3

Этот метод работает от вызова метода async, а также от обычного метода. (С# 5)

/// <summary>
///     Returns Current method name
/// </summary>
/// <returns>callers method name</returns>
public string GetCurrentMethod([CallerMemberName] string callerName = "")
{
    return callerName;
}

Ответ 4

Вам нужно записать имя метода в начале метода async, где-то перед первым асинхронным вызовом. Наиболее удобным способом, который я обнаружил, чтобы пропустить мимо созданного компилятором конечного автомата, является просмотр типа объявления каждого метода в трассировке стека.

var method = new StackTrace()
    .GetFrames()
    .Select(frame => frame.GetMethod())
    .FirstOrDefault(item => item.DeclaringType == GetType());
await Task.Yield();
if (method != null)
{
    Console.WriteLine(method.Name);
}

Ответ 5

Немного поздно ответить на ваш вопрос, я думаю, но так как у меня была такая же проблема, и мне нужно было разобраться в этом из-за отсутствия хорошего разрешения на это в Интернете, вот что я сделал, и это работает отлично, по крайней мере для меня -

  • Определите следующее Regex где-нибудь, к которому могут обращаться все вызывающие, поскольку Regex объекты должны быть проанализированы перед их использованием; следовательно, это может быть дорогостоящим, и это не очень хорошая практика, чтобы создавать их снова и снова, если мы можем использовать один и тот же. public static readonly Regex ASYNC_METHOD_NAME_FORMAT = new Regex(@"^\<(?\w+)>\w+?");
  • Используйте следующий код, чтобы получить имя метода
    string methodName = ASYNC_METHOD_NAME_FORMAT.Match(MethodBase.GetCurrentMethod().ReflectedType.Name).Groups["method_name"].Value

Надеюсь, это поможет!

Ответ 6

Вы можете отправить strackTrace.GetFrame(i).GetMethod(), который является объектом System.Reflection.MethodBase для функции, которая проверяет, имеет ли System.Reflection.MethodBase.DeclaringType.FullName символы < > и если так получится имя запрошенного метода, которое лежит между символами <... > .

static private string getMethodName(System.Reflection.MethodBase method)
    {
        string _methodName = method.DeclaringType.FullName;

        if (_methodName.Contains(">") || _methodName.Contains("<"))
        {
            _methodName = _methodName.Split('<', '>')[1];
        }
        else
        {
            _methodName = method.Name;
        }

        return _methodName;
    }

И пример использования:

var _stackTrace = new System.Diagnostics.StackTrace(exception, true);
string _methodName = getMethodName(_stackTrace.GetFrame(0).GetMethod());

Ответ 7

Я использовал смесь ответов Натана и Майка.

Использование GetType() для запроса метода из трассировки стека не работает для меня. Не гарантировано. Но использование CallerMemberNameAttrinute позволяет мне получить точный метод по его имени.

Так что мой код будет:

using System.Runtime.CompilerServices;

public static void Main(string[] args)
{
    Test().Wait();            
}

private static async Task Test()
{
    await Task.Yield();
    Log();
    await Task.Yield();
}

private static void Log([CallerMemberName]string methodName = "")
{
   var method = new StackTrace()
       .GetFrames()
       .Select(frame => frame.GetMethod())
       .FirstOrDefault(item => item.Name == methodName);

   Console.WriteLine("Log: {0}", method.DeclaringType + "." + method.Name);
}

Таким образом, я получаю имя метода с полным путем к пространству имен.