Можете ли вы использовать отражение, чтобы найти имя текущего исполняемого метода?

Как и заголовок говорит: Может ли отражение отразить имя текущего исполняемого метода.

Я склонен угадывать, из-за проблемы Гейзенберга. Как вы вызываете метод, который скажет вам текущий метод, не изменяя текущий метод? Но я надеюсь, что кто-то может доказать мне, что там не так.

Обновление:

  • Часть 2: Может ли это использоваться для просмотра внутри кода для свойства?
  • Часть 3: Каким будет представление производительности?

Конечный результат
Я узнал о MethodBase.GetCurrentMethod(). Я также узнал, что не только могу создать трассировку стека, я могу создать только точный кадр, который мне нужен, если захочу.

Чтобы использовать это внутри свойства, просто возьмите .Substring(4), чтобы удалить "set_" или "get_".

Ответы

Ответ 1

Начиная с .NET 4.5 вы также можете использовать [CallerMemberName]

Пример: установщик свойства (для ответа на часть 2):

    protected void SetProperty<T>(T value, [CallerMemberName] string property = null)
    {
        this.propertyValues[property] = value;
        OnPropertyChanged(property);
    }

    public string SomeProperty
    {
        set { SetProperty(value); }
    }

Компилятор будет предоставлять совпадающие строковые литералы на сайтах вызовов, так что производительность практически не снижается.

Ответ 3

Фрагмент, предоставленный Лексом, был немного длинным, поэтому я указываю важную роль, поскольку никто другой не использовал точно такую ​​же технику:

string MethodName = new StackFrame(0).GetMethod().Name;

Это должно возвращать идентичные результаты методу MethodBase.GetCurrentMethod(). Name, но все же стоит указать, потому что я мог реализовать это один раз в своем собственном методе с использованием индекса 1 для предыдущего метода и вызвать его из нескольких разных свойства. Кроме того, он возвращает только один кадр, а не всю трассировку стека:

private string GetPropertyName()
{  //.SubString(4) strips the property prefix (get|set) from the name
    return new StackFrame(1).GetMethod().Name.Substring(4);
}

Это тоже однострочный;)

Ответ 4

Попробуйте это внутри метода Main в пустой консольной программе:

MethodBase method = MethodBase.GetCurrentMethod();
Console.WriteLine(method.Name);

Консольный выход:
Main

Ответ 5

Да, безусловно.

Если вы хотите, чтобы объект манипулировал, я действительно использую такую ​​функцию:

public static T CreateWrapper<T>(Exception innerException, params object[] parameterValues) where T : Exception, new()
{
    if (parameterValues == null)
    {
        parameterValues = new object[0];
    }

    Exception exception   = null;
    StringBuilder builder = new StringBuilder();
    MethodBase method     = new StackFrame(2).GetMethod();
    ParameterInfo[] parameters = method.GetParameters();
    builder.AppendFormat(CultureInfo.InvariantCulture, ExceptionFormat, new object[] { method.DeclaringType.Name, method.Name });
    if ((parameters.Length > 0) || (parameterValues.Length > 0))
    {
        builder.Append(GetParameterList(parameters, parameterValues));
    }

    exception = (Exception)Activator.CreateInstance(typeof(T), new object[] { builder.ToString(), innerException });
    return (T)exception;
}

Эта строка:

MethodBase method     = new StackFrame(2).GetMethod();

Продвигает фрейм стека, чтобы найти вызывающий метод, затем мы используем отражение для получения значений информации о параметрах, переданных ему для общей функции сообщения об ошибках. Чтобы получить текущий метод, просто используйте текущий кадр стека (1).

Как уже говорилось, для имени текущего метода вы также можете использовать:

MethodBase.GetCurrentMethod()

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

Post 4.5 теперь вы можете использовать [CallerMemberNameAttribute] как часть параметров метода, чтобы получить строку имени метода - это может помочь в некоторых сценариях (но на самом деле в примере выше)

public void Foo ([CallerMemberName] string methodName = null)

Это, казалось, было главным образом решением для поддержки INotifyPropertyChanged, где раньше у вас были строки, заполненные всем кодом вашего события.

Ответ 6

Сравнение способов получения имени метода - использование конструкции произвольного времени в LinqPad:

КОД

void Main()
{
    // from http://blogs.msdn.com/b/webdevelopertips/archive/2009/06/23/tip-83-did-you-know-you-can-get-the-name-of-the-calling-method-from-the-stack-using-reflection.aspx
    // and https://stackoverflow.com/questions/2652460/c-sharp-how-to-get-the-name-of-the-current-method-from-code

    var fn = new methods();

    fn.reflection().Dump("reflection");
    fn.stacktrace().Dump("stacktrace");
    fn.inlineconstant().Dump("inlineconstant");
    fn.constant().Dump("constant");
    fn.expr().Dump("expr");
    fn.exprmember().Dump("exprmember");
    fn.callermember().Dump("callermember");

    new Perf {
        { "reflection", n => fn.reflection() },
        { "stacktrace", n => fn.stacktrace() },
        { "inlineconstant", n => fn.inlineconstant() },
        { "constant", n => fn.constant() },
        { "expr", n => fn.expr() },
        { "exprmember", n => fn.exprmember() },
        { "callermember", n => fn.callermember() },
    }.Vs("Method name retrieval");
}

// Define other methods and classes here
class methods {
    public string reflection() {
        return System.Reflection.MethodBase.GetCurrentMethod().Name;
    }
    public string stacktrace() {
        return new StackTrace().GetFrame(0).GetMethod().Name;
    }
    public string inlineconstant() {
        return "inlineconstant";
    }
    const string CONSTANT_NAME = "constant";
    public string constant() {
        return CONSTANT_NAME;
    }
    public string expr() {
        Expression<Func<methods, string>> ex = e => e.expr();
        return ex.ToString();
    }
    public string exprmember() {
        return expressionName<methods,string>(e => e.exprmember);
    }
    protected string expressionName<T,P>(Expression<Func<T,Func<P>>> action) {
        // https://stackoverflow.com/a/9015598/1037948
        return ((((action.Body as UnaryExpression).Operand as MethodCallExpression).Object as ConstantExpression).Value as MethodInfo).Name;
    }
    public string callermember([CallerMemberName]string name = null) {
        return name;
    }
}

РЕЗУЛЬТАТЫ

отражение отражение

StackTrace трассировка стека

inlineconstant встроенная константа

постоянная постоянная

выраж e => e.expr()

exprmember exprmember

callermember Главная

Method name retrieval: (reflection) vs (stacktrace) vs (inlineconstant) vs (constant) vs (expr) vs (exprmember) vs (callermember) 

 154673 ticks elapsed ( 15.4673 ms) - reflection
2588601 ticks elapsed (258.8601 ms) - stacktrace
   1985 ticks elapsed (  0.1985 ms) - inlineconstant
   1385 ticks elapsed (  0.1385 ms) - constant
1366706 ticks elapsed (136.6706 ms) - expr
 775160 ticks elapsed ( 77.516  ms) - exprmember
   2073 ticks elapsed (  0.2073 ms) - callermember


>> winner: constant

Обратите внимание, что методы expr и callermember не совсем "правильные". И там вы видите повторение соответствующего комментария о том, что отражение в ~ 15 раз быстрее, чем трассировка стека.

Ответ 7

EDIT: MethodBase, вероятно, лучший способ просто получить метод, в котором вы находитесь (в отличие от всего вызывающего стека). Тем не менее, я все равно буду беспокоиться о вложениях.

Вы можете использовать StackTrace в рамках метода:

StackTrace st = new StackTrace(true);

И посмотрите на рамки:

// The first frame will be the method you want (However, see caution below)
st.GetFrames();

Однако имейте в виду, что если метод встроен, вы не будете внутри метода, который, по вашему мнению, вы. Вы можете использовать атрибут, чтобы предотвратить inlining:

[MethodImpl(MethodImplOptions.NoInlining)]

Ответ 8

Простым способом решения является:

System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.FullName + "." + System.Reflection.MethodBase.GetCurrentMethod().Name;

Если System.Reflection включен в блок использования:

MethodBase.GetCurrentMethod().DeclaringType.FullName + "." + MethodBase.GetCurrentMethod().Name;

Ответ 9

Как насчет этого:

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

Попробуйте это...

    /// <summary>
    /// Return the full name of method
    /// </summary>
    /// <param name="obj">Class that calls this method (use Report(this))</param>
    /// <returns></returns>
    public string Report(object obj)
    {
        var reflectedType = new StackTrace().GetFrame(1).GetMethod().ReflectedType;
        if (reflectedType == null) return null;

        var i = reflectedType.FullName;
        var ii = new StackTrace().GetFrame(1).GetMethod().Name;

        return string.Concat(i, ".", ii);
    }

Ответ 13

Я просто сделал это с помощью простого статического класса:

using System.Runtime.CompilerServices;
.
.
.
    public static class MyMethodName
        {
            public static string Show([CallerMemberName] string name = "")
            {
                return name;
            }
        }

то в вашем коде:

private void button1_Click(object sender, EventArgs e)
        {
            textBox1.Text = MyMethodName.Show();
        }

        private void button2_Click(object sender, EventArgs e)
        {
            textBox1.Text = MyMethodName.Show();
        }

Ответ 14

new StackTrace(). ToString(). Split ("\ r\n", StringSplitOptions.RemoveEmptyEntries) [0].Replace("at", ""). Trim()