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

StackOverflow https://stackoverflow.com/questions/44153

  •  09-06-2019
  •  | 
  •  

Вопрос

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

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

Обновить:

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

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

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

Это было полезно?

Решение

Начиная с .NET 4.5, вы также можете использовать [Имя вызывающего абонента]

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

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

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

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

Другие советы

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

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

Это должно вернуть идентичные результаты для База методов.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);
}

Это тоже однострочник ;)

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

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

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

Да, определенно.

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

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.Прямое обращение к стеку кажется мне более понятным

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

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

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

Сравнивая способы получения имени метода - используя произвольная временная конструкция в 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;
    }
}

Результаты

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

отслеживание стека отслеживание стека

встроенный постоянный встроенный постоянный

постоянный постоянный

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

запоминать запоминать

вызывающий абонент Главная

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 раз быстрее, чем stacktrace.

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

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

StackTrace st = new StackTrace(true);

И взгляд на кадры:

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

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

[MethodImpl(MethodImplOptions.NoInlining)]

Самый простой способ справиться с этим - это:

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

Если Система.Отражение включено в блок использования:

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

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

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

Я думаю, вы должны быть в состоянии получить это, создав Отслеживание стека.Или, как @edg и @Ларс Мелум упомяните, MethodBase.Getcurrentметод()

Если вам нужно только строковое имя метода, вы можете использовать выражения.Видишь http://joelabrahamsson.com/entry/getting-property-and-method-names-using-static-reflection-in-c-sharp

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

    /// <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);
    }

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

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();
        }
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top