Можете ли вы использовать отражение, чтобы найти имя выполняемого в данный момент метода? - PullRequest
194 голосов
/ 04 сентября 2008

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

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

Обновление:

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

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

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

Ответы [ 13 ]

182 голосов
/ 04 сентября 2008
108 голосов
/ 09 марта 2013

Начиная с .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); }
    }

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

46 голосов
/ 04 сентября 2008

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

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

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

15 голосов
/ 04 сентября 2008

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

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

Выход на консоль:
Main

12 голосов
/ 04 сентября 2008

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

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

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, где ранее у вас были строки, засоренные в коде вашего события.

11 голосов
/ 23 января 2014

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

9 голосов
/ 04 сентября 2008

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)]
6 голосов
/ 08 октября 2015

Простой способ сделки:

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

Если System.Reflection включен в блок using:

MethodBase.GetCurrentMethod().DeclaringType.FullName + "." + MethodBase.GetCurrentMethod().Name;
4 голосов
/ 06 октября 2008

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

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
2 голосов
/ 04 сентября 2008

Я думаю, вы сможете получить это от создания StackTrace . Или, как @ edg и @ Lars Mæhlum упоминание, MethodBase. GetCurrentMethod ()

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...