Pouvez-vous utiliser la réflexion pour trouver le nom de la méthode en cours d’exécution ?

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

  •  09-06-2019
  •  | 
  •  

Question

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

J'ai tendance à penser que non, à cause du problème d'Heisenberg.Comment appeler une méthode qui vous indiquera la méthode actuelle sans changer quelle est la méthode actuelle ?Mais j'espère que quelqu'un pourra me prouver le contraire.

Mise à jour:

  • Partie 2:Cela pourrait-il également être utilisé pour rechercher dans le code d'une propriété ?
  • Partie 3 :À quoi ressemblerait la performance ?

Résultat final
J'ai découvert MethodBase.GetCurrentMethod().J'ai également appris que non seulement je peux créer une trace de pile, mais que je peux également créer uniquement le cadre exact dont j'ai besoin si je le souhaite.

Pour l'utiliser dans une propriété, prenez simplement un .Substring(4) pour supprimer le 'set_' ou 'get_'.

Était-ce utile?

La solution

Depuis .NET 4.5, vous pouvez également utiliser [Nom du membre appelant]

Exemple:un poseur de propriétés (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 sur les sites d'appel, il n'y a donc pratiquement aucune surcharge de performances.

Autres conseils

L'extrait fourni par Lex était un peu long, je souligne donc 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 au MethodBase.GetCurrentMethod().Name technique, mais cela vaut quand même la peine de le souligner car je pourrais l'implémenter une fois dans sa propre méthode en utilisant l'index 1 pour le précédent méthode et appelez-la à partir d’un certain nombre de propriétés différentes.De plus, il ne renvoie qu'une seule image plutôt que la totalité de la trace de la pile :

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 une one-liner ;)

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

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

Sortie console :
Main

Oui définitivement.

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

Parcourt le cadre de pile pour trouver la méthode appelante, puis nous utilisons la réflexion pour obtenir les valeurs d'informations sur les paramètres qui lui sont transmises pour une fonction générique de rapport d'erreurs.Pour obtenir la méthode actuelle, utilisez simplement le cadre de pile actuel (1) à la place.

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

MethodBase.GetCurrentMethod()

Je préfère parcourir la pile car si vous regardez cette méthode en interne, cela crée simplement un StackCrawlMark de toute façon.Aborder directement la Stack 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 la prise en charge de INotifyPropertyChanged où auparavant vous aviez des chaînes éparpillées tout au long de votre code d'événement.

Comparaison des façons d'obtenir le nom de la méthode -- en utilisant un 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

réflexionréflexion

trace de la piletrace de la pile

constante en ligneconstante en ligne

constanteconstante

expressione => e.expr()

membre exprmembre expr

membre appelantPrincipal

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

Notez que le expr et callermember les méthodes ne sont pas tout à fait « bonnes ».Et là vous voyez une répétition de un commentaire connexe cette réflexion est ~ 15 fois plus rapide que stacktrace.

MODIFIER:MethodBase est probablement un meilleur moyen d'obtenir simplement la méthode dans laquelle vous vous trouvez (par opposition à l'ensemble de la pile d'appels).Cependant, je serais toujours préoccupé par l'inline.

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 dans la méthode que vous pensez être.Vous pouvez utiliser un attribut pour empêcher l'inline :

[MethodImpl(MethodImplOptions.NoInlining)]

La façon simple de traiter est la suivante :

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

Si System.Reflection est inclus dans le bloc using :

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

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

Je pense que vous devriez pouvoir obtenir cela en créant un Trace de la pile.Ou, comme @bord et @Lars Maehlum mentionner, MéthodeBase.Obtenir la méthode actuelle()

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

Essaye ça...

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

Je viens de faire ça avec une simple classe statique :

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

puis dans ton 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();
        }
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top