Puoi usare la riflessione per trovare il nome del metodo attualmente in esecuzione?

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

  •  09-06-2019
  •  | 
  •  

Domanda

Come dice il titolo:Reflection può darti il ​​nome del metodo attualmente in esecuzione.

Sono propenso a supporre di no, a causa del problema di Heisenberg.Come si chiama un metodo che ti dirà il metodo corrente senza modificare quale sia il metodo corrente?Ma spero che qualcuno possa dimostrarmi che sbaglio.

Aggiornamento:

  • Parte 2:Questo potrebbe essere usato anche per cercare all'interno del codice di una proprietà?
  • Parte 3:Come sarebbe la performance?

Risultato finale
Ho imparato a conoscere MethodBase.GetCurrentMethod().Ho anche imparato che non solo posso creare uno stack trace, ma posso creare solo il frame esatto di cui ho bisogno, se lo desidero.

Per usarlo all'interno di una proprietà, basta prendere un .Substring(4) per rimuovere 'set_' o 'get_'.

È stato utile?

Soluzione

A partire da .NET 4.5 è anche possibile utilizzare [NomeMembroChiamante]

Esempio:un setter di proprietà (per rispondere alla parte 2):

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

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

Il compilatore fornirà valori letterali stringa corrispondenti nei siti di chiamata, quindi sostanzialmente non vi è alcun sovraccarico sulle prestazioni.

Altri suggerimenti

Lo snippet fornito da Lex era un po' lungo, quindi sottolineo la parte importante poiché nessun altro ha utilizzato la stessa identica tecnica:

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

Ciò dovrebbe restituire risultati identici a MethodBase.GetCurrentMethod().Nome tecnica, ma vale comunque la pena sottolinearlo perché potrei implementarlo una volta nel suo metodo utilizzando l'indice 1 per il precedente metodo e chiamarlo da una serie di proprietà diverse.Inoltre, restituisce solo un fotogramma anziché l'intera traccia dello stack:

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

Anche questa è una battuta ;)

Prova questo all'interno del metodo Main in un programma console vuoto:

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

Uscita console:
Main

Sì, sicuramente.

Se vuoi che un oggetto venga manipolato, in realtà utilizzo una funzione come questa:

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

Questa riga:

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

Risale lo stack frame per trovare il metodo chiamante, quindi utilizziamo la riflessione per ottenere i valori delle informazioni sui parametri passati ad esso per una funzione generica di segnalazione degli errori.Per ottenere il metodo corrente è sufficiente utilizzare invece lo stack frame corrente (1).

Come altri hanno detto per il nome del metodo attuale puoi anche usare:

MethodBase.GetCurrentMethod()

Preferisco percorrere lo stack perché se guardi internamente quel metodo crea comunque semplicemente uno StackCrawlMark.Affrontare direttamente lo Stack mi sembra più chiaro

Dopo la versione 4.5 ora puoi utilizzare [CallerMemberNameAttribute] come parte dei parametri del metodo per ottenere una stringa del nome del metodo: questo può essere d'aiuto in alcuni scenari (ma in realtà nell'esempio sopra)

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

Questa sembrava essere principalmente una soluzione per il supporto INotifyPropertyChanged in cui in precedenza avevi stringhe disseminate in tutto il codice evento.

Confronto tra i modi per ottenere il nome del metodo: utilizzando an costrutto temporale arbitrario in LinqPad:

CODICE

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

RISULTATI

riflessioneriflessione

stacktracestacktrace

inlinecostanteinlinecostante

costantecostante

espre => e.espr()

exprmemberexprmember

callermemberPrincipale

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

Si noti che il expr E callermember i metodi non sono del tutto "giusti".E lì vedi una ripetizione di un commento correlato quella riflessione è ~ 15 volte più veloce dello stacktrace.

MODIFICARE:MethodBase è probabilmente un modo migliore per ottenere semplicemente il metodo in cui ti trovi (al contrario dell'intero stack di chiamate).Sarei comunque preoccupato per l'inlining.

Puoi utilizzare StackTrace all'interno del metodo:

StackTrace st = new StackTrace(true);

E lo sguardo ai fotogrammi:

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

Tuttavia, tieni presente che se il metodo è in linea, non sarai all'interno del metodo che pensi di essere.Puoi utilizzare un attributo per impedire l'inlining:

[MethodImpl(MethodImplOptions.NoInlining)]

Il modo più semplice per gestire è:

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

Se System.Reflection è incluso nel blocco using:

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

Che ne dici di questo:

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

Penso che dovresti essere in grado di ottenerlo creando un file StackTrace.Oppure, come @bordo E @Lars Maehlum menzione, MethodBase.Ottieni metodo corrente()

Se hai bisogno solo del nome stringa del metodo, puoi utilizzare Expressions.Vedere http://joelabrahamsson.com/entry/getting-property-and-method-names-using-static-reflection-in-c-sharp

Prova questo...

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

L'ho appena fatto con una semplice classe statica:

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

quindi nel tuo codice:

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

        private void button2_Click(object sender, EventArgs e)
        {
            textBox1.Text = MyMethodName.Show();
        }
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top