Чи можете ви використати роздуми, щоб знайти ім'я поточного методу виконання?


202

Як заголовок зазначає: Чи може відображення дати вам ім'я поточного методу виконання.

Я схильний не здогадуватися, через проблему Гейзенберга. Як ви називаєте метод, який підкаже вам поточний метод, не змінюючи того, що є поточним методом? Але я сподіваюся, що хтось може довести мене неправильно там.

Оновлення:

  • Частина 2: Чи можна це використовувати і для пошуку внутрішнього коду для властивості?
  • Частина 3: Яким буде виступ?

Підсумковий результат
Я дізнався про MethodBase.GetCurrentMethod (). Я також дізнався, що не тільки я можу створити слід стека, я можу створити лише той потрібний кадр, який мені потрібен, якщо я хочу.

Щоб використовувати це всередині властивості, просто візьміть .Substring (4), щоб видалити 'set_' або 'get_'.


Джоел, я знаю, це давнє питання, але що ти маєш на увазі, створюючи точний кадр методу?
Abhijeet

Він посилається на конкретний елемент стека викликів: частина сліду стека, яка має значення.
Joel Coehoorn

Відповіді:


119

Станом на .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
Це чудово! Я використовував StackFrame(1)метод, описаний в інших відповідях для ведення журналу, який, здавалося, працює, поки Джиттер не вирішив почати вкладати речі. Я не хотів додавати атрибут для запобігання вбудованим вказівкам з міркувань продуктивності. Використання [CallerMemberName]підходу вирішило проблему. Дякую!
Брайан Роджерс

5
[CallerMemberName] доступний у мережі 4 із
упаковкою

2
Враховуйте, що використання StackFrame (1) в режимі налагодження повинно працювати. Але при використанні режиму випуску при компілюванні можуть бути деякі оптимізації, і стек може бути не таким, як ви очікуєте.
Аксель О'Коннелл

Чи не поверне це викликовий член (тобто SomeProperty), а не метод, який виконується в даний час?
Леннарт

1
Так, виклик налаштування призведе до виклику OnPropertyChanged("SomeProperty")та неOnPropertyChanged("SetProperty")
Джона Нільссона

189

Для неметодів asyncможна використовувати

System.Reflection.MethodBase.GetCurrentMethod().Name;

https://docs.microsoft.com/en-us/dotnet/api/system.reflection.methodbase.getcurrentmethod

Пам'ятайте, що для asyncметодів він поверне "MoveNext".


8
Майте на увазі, що це не завжди дає очікуваних результатів. Тобто, малі методи або властивості часто вказуються у версії версій, і в цьому випадку результатом буде замість назви методу абонента.
Авель

5
Наскільки я знаю, ні. Тому що під час виконання MSIL більше не доступний у покажчику виконання (це JITted). Ви все ще можете використовувати рефлексію, якщо знаєте назву методу. Річ у тім, що, в даний час метод виконання в даний час є іншим методом (тобто, один або більше вище стека). Іншими словами, метод зник. Навіть якщо ви позначите свій метод за допомогою NoInlining, все ж є ймовірність, що він буде оптимізований для виклику хвоста, і в цьому випадку він також пропаде. Він буде працювати, однак, під час створення налагодження.
Авель

1
Щоб уникнути вбудовування, додайте атрибут [MethodImpl (MethodImplOptions.NoInlining)] поверх методу.
alex.peter

Всередині asyncметоду ви, швидше за все, отримаєте "MoveNext" як назву методу.
Віктор Ярема

46

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

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

Це має повернути однакові результати у MethodBase.GetCurrentMethod (). Метод імені, але це все-таки варто зазначити, тому що я міг би реалізувати це один раз у своєму власному методі, використовуючи індекс 1 для попереднього методу і викликати його з кількох різних властивостей. Крім того, він повертає лише один кадр, а не весь слід стека:

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

Це однолінійний теж;)


може бути загальнодоступним статичним рядком GetPropertyName () у хелперному класі? статичний метод?
Кікенет

2
Те саме, що і у відповіді Еда Гінесса: стек може бути різним у складах випусків, і перший метод може бути не таким, як поточний метод у випадках вбудовування чи оптимізації виклику хвоста.
Авель

Дивіться відповідь Джона Нілссона про приємний спосіб вирішити проблему, якщо ви використовуєте .Net 4.5.
Брайан Роджерс

це може бути краще, ніж прийнята відповідь і вище відповідь
Т.Тодуа

16

Спробуйте це в методі Main в порожній програмі консолі:

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

Вихід з консолі:
Main


12

Однозначно так.

Якщо ви хочете, щоб об'єкт маніпулював, я фактично використовую таку функцію:

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, де раніше у вас були вписані рядки через ваш код події.


Не дурний; Я просто передав їх. Ви, напевно, могли б зробити щось, щоб зробити його простішим, але співвідношення зусиль до винагороди, здавалося б, сприяло простоті. По суті, dev просто копіює у списку параметрів підпис методу (видаляючи типи курсу).
Лекс

що це: ExceptionFormat та GetParameterList?
Кікенет

Запізнення у відповіді, але: ExceptionFormat - це постійний формат рядків, а GetParameterList - це просто функція, яка форматує параметри зі значеннями (ви могли це зробити в рядку)
Lex

11

Порівнюючи способи отримання назви методу - використовуючи довільну конструкцію синхронізації в 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 /programming/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 stacktrace

inlineconstant inlineconstant

постійна постійна

expr e => e.expr ()

exprmember exprmember

Callermember Main

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 разів швидше, ніж стеження.


9

EDIT: 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)]

Inline завдяки оптимізації випуску особливо складний, оскільки код буде поводитися по-різному в конфігураціях налагодження та випуску. Слідкуйте за невеликими властивостями, вони - найімовірніші жертви цього.
ДК.

Цікаво, чому б ти використовував new StackTrace(true)замість цього new StackTrace(false). Встановивши, що to trueпризведе до сліду стека при спробі захоплення імені файлу, номера рядка тощо, що може зробити цей дзвінок повільнішим. В іншому випадку приємна відповідь
Івайло Славов

6

Найпростіший спосіб вирішити:

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

Якщо System.Reflection включений у блок використання:

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

4

Як щодо цього:

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


0

Спробуйте це...

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

0

Я щойно робив це за допомогою простого статичного класу:

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.