Pouvez-vous utiliser reflection pour trouver le nom de la méthode en cours d'exécution?

Comme le titre dit: la réflexion peut-elle vous donner le nom de la méthode en cours d'exécution.

Je suis enclin à deviner non, à cause du problème de Heisenberg. Comment appelez-vous une méthode qui vous indiquera la méthode actuelle sans changer ce que la méthode actuelle est? Mais j'espère que quelqu'un pourra me prouver le contraire.

Mise à Jour:

  • Partie 2: Cela pourrait-il être utilisé pour regarder à l'intérieur du code pour une propriété?
  • Partie 3: Quelle serait la performance être comme?

Résultat Final
J'ai appris à propos de MethodBase.GetCurrentMethod (). J'ai également appris que non seulement je peux créer une trace de pile, je peux créer seulement le cadre exact dont j'ai besoin si je veux.

Pour l'utiliser dans une propriété, il suffit de prendre un .Sous-chaîne (4) pour supprimer le 'set_' ou 'get_'.

184
demandé sur Joel Coehoorn 2008-09-04 20:45:35

13 réponses

Comme de .NET 4.5, vous pouvez également utiliser [CallerMemberName]

Exemple: un setter de propriété (pour répondre à la partie 2):

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

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

Le compilateur fournira des littéraux de chaîne correspondants aux callsites, donc il n'y a fondamentalement pas de surcharge de performance.

99
répondu John Nilsson 2018-01-24 22:15:23
176
répondu Ed Guiness 2011-08-26 21:18:29

L'extrait fourni par Lex était un peu long, donc je signale la partie importante puisque personne d'autre n'a utilisé exactement la même technique:

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

Cela devrait renvoyer des résultats identiques à la techniqueMethodBase.GetCurrentMethod().Name , mais cela vaut toujours la peine d'être souligné car je pourrais l'implémenter une fois dans sa propre méthode en utilisant l'index 1 pour la méthode précédente et l'appeler à partir d'un certain nombre de propriétés différentes. En outre, il ne renvoie qu'une image plutôt que le trace de pile entière:

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

C'est aussi un one-liner ;)

45
répondu Joel Coehoorn 2008-11-12 15:53:51

Essayez ceci dans la méthode principale dans un programme de console vide:

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

Sortie De La Console:
Main

14
répondu Lars Mæhlum 2008-09-04 16:52:03

Oui certainement.

Si vous voulez qu'un objet soit manipulé, j'utilise en fait une fonction comme celle-ci:

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

Cette ligne:

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

Remonte le cadre de la pile pour trouver la méthode d'appel, puis nous utilisons la réflexion pour obtenir des valeurs d'information de paramètre qui lui sont transmises pour une fonction de rapport d'erreur générique. Pour obtenir la méthode actuelle, utilisez simplement current stack frame (1) à la place.

Comme d'autres l'ont dit pour le nom des méthodes actuelles, vous pouvez également utiliser:

MethodBase.GetCurrentMethod()

Je préfère marcher sur la pile parce que si vous regardez en interne cette méthode, il crée simplement un StackCrawlMark de toute façon. Adresser directement la pile me semble plus clair

Post 4.5 vous pouvez maintenant utiliser le [CallerMemberNameAttribute] dans le cadre des paramètres de la méthode pour obtenir une chaîne du nom de la méthode-cela peut aider dans certains scénarios (mais vraiment dans l'exemple ci-dessus)

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

Cela semblait être principalement une solution pour le support INotifyPropertyChanged où auparavant vous aviez des chaînes jonchées grâce à votre code d'événement.

12
répondu Lex 2013-06-27 10:23:22

EDIT: MethodBase est probablement un meilleur moyen d'obtenir la méthode dans laquelle vous vous trouvez (par opposition à toute la pile appelante). Je serais toujours préoccupé par l'inlining cependant.

Vous pouvez utiliser un StackTrace dans la méthode:

StackTrace st = new StackTrace(true);

Et le regard sur les cadres:

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

Cependant, sachez que si la méthode est inline, vous ne serez pas à l'intérieur de la méthode que vous pensez de vous. Vous pouvez utiliser un attribut pour empêcher l'inlining:

[MethodImpl(MethodImplOptions.NoInlining)]
9
répondu denis phillips 2008-09-04 16:52:40

Comparer les façons d'obtenir le nom de la méthode - en utilisant une construction de synchronisation arbitraire dans LinqPad:

CODE

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

Résultats

La Réflexion réflexion

Stacktrace stacktrace

Inlineconstant inlineconstant

Constante constante

Expr E = > E. expr ()

Exprmember exprmember

Callermember Principal

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

Note que les méthodes expr et callermember ne sont pas tout à fait"correctes". Et là, vous voyez une répétition de un commentaire connexe {[6] } que la réflexion est ~15x plus rapide que stacktrace.

9
répondu drzaus 2017-05-23 12:26:20

Que diriez-vous de ceci:

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
4
répondu jesal 2008-10-05 20:11:13

La façon simple de traiter est:

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

Si le système.La réflexion est incluse dans le bloc using:

MethodBase.GetCurrentMethod().DeclaringType.FullName + "." + MethodBase.GetCurrentMethod().Name;
4
répondu Shar 2016-12-14 19:04:12

Je pense que vous devriez pouvoir obtenir cela en créant un StackTrace . Ou, comme @gde et @Lars Mæhlum mentionner, MethodBase. GetCurrentMethod()

2
répondu bdukes 2017-05-23 12:02:39

Si vous n'avez besoin que du nom de chaîne de la méthode, vous pouvez utiliser des Expressions. Tu vois http://joelabrahamsson.com/entry/getting-property-and-method-names-using-static-reflection-in-c-sharp

0
répondu awgtek 2012-10-02 13:46:44

Essayez ceci...

    /// <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
répondu Adriano silva ribeiro 2015-12-29 06:52:21

Je viens de le faire avec une simple classe statique:

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

Puis dans votre code:

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

        private void button2_Click(object sender, EventArgs e)
        {
            textBox1.Text = MyMethodName.Show();
        }
0
répondu michael kosak 2017-08-09 14:52:29