Können Sie mithilfe von Reflection den Namen der aktuell ausgeführten Methode ermitteln?

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

  •  09-06-2019
  •  | 
  •  

Frage

Wie der Titel schon sagt:Kann Reflection Ihnen den Namen der aktuell ausgeführten Methode geben?

Aufgrund des Heisenberg-Problems neige ich dazu, dies nicht zu vermuten.Wie rufen Sie eine Methode auf, die Ihnen die aktuelle Methode mitteilt, ohne die aktuelle Methode zu ändern?Aber ich hoffe, dass mir dort jemand das Gegenteil beweisen kann.

Aktualisieren:

  • Teil 2:Könnte dies auch verwendet werden, um im Code nach einer Eigenschaft zu suchen?
  • Teil 3:Wie würde die Aufführung aussehen?

Endergebnis
Ich habe etwas über MethodBase.GetCurrentMethod() gelernt.Ich habe auch gelernt, dass ich nicht nur einen Stack-Trace erstellen kann, sondern bei Bedarf auch nur den genauen Frame erstellen kann, den ich benötige.

Um dies innerhalb einer Eigenschaft zu verwenden, nehmen Sie einfach einen .Substring(4), um „set_“ oder „get_“ zu entfernen.

War es hilfreich?

Lösung

Ab .NET 4.5 können Sie auch verwenden [CallerMemberName]

Beispiel:ein Eigenschaftssetzer (um Teil 2 zu beantworten):

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

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

Der Compiler stellt an Aufrufstellen passende String-Literale bereit, sodass grundsätzlich kein Leistungsaufwand entsteht.

Andere Tipps

Der von Lex bereitgestellte Ausschnitt war etwas lang, daher weise ich auf den wichtigen Teil hin, da niemand sonst genau die gleiche Technik verwendet hat:

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

Dies sollte identische Ergebnisse liefern MethodBase.GetCurrentMethod().Name Technik, aber es lohnt sich trotzdem, darauf hinzuweisen, da ich dies einmal in einer eigenen Methode implementieren könnte, indem ich Index 1 für verwende vorherige Methode und rufen Sie sie aus einer Reihe verschiedener Eigenschaften auf.Außerdem wird nur ein Frame zurückgegeben und nicht der gesamte Stack-Trace:

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

Es ist auch ein Einzeiler ;)

Versuchen Sie dies innerhalb der Main-Methode in einem leeren Konsolenprogramm:

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

Konsolenausgabe:
Main

Ja auf jeden Fall.

Wenn Sie möchten, dass ein Objekt manipuliert wird, verwende ich tatsächlich eine Funktion wie diese:

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

Diese Linie:

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

Wir gehen den Stapelrahmen nach oben, um die aufrufende Methode zu finden. Anschließend verwenden wir Reflektion, um Parameterinformationswerte zu erhalten, die für eine generische Fehlerberichtsfunktion an sie übergeben werden.Um die aktuelle Methode zu erhalten, verwenden Sie stattdessen einfach den aktuellen Stapelrahmen (1).

Wie andere bereits gesagt haben, können Sie für den aktuellen Methodennamen auch Folgendes verwenden:

MethodBase.GetCurrentMethod()

Ich bevorzuge es, durch den Stapel zu gehen, denn wenn man sich diese Methode intern anschaut, wird ohnehin einfach ein StackCrawlMark erstellt.Den Stack direkt anzusprechen erscheint mir klarer

Nach 4.5 können Sie jetzt das [CallerMemberNameAttribute] als Teil der Methodenparameter verwenden, um eine Zeichenfolge des Methodennamens abzurufen – dies kann in einigen Szenarien hilfreich sein (aber wirklich im obigen Beispiel).

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

Dies schien hauptsächlich eine Lösung für die INotifyPropertyChanged-Unterstützung zu sein, bei der zuvor überall in Ihrem Ereigniscode Zeichenfolgen verstreut waren.

Vergleich der Möglichkeiten, den Methodennamen zu erhalten – mithilfe eines beliebiges Timing-Konstrukt in 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;
    }
}

ERGEBNISSE

BetrachtungBetrachtung

StacktraceStacktrace

InlinekonstanteInlinekonstante

KonstanteKonstante

Ausdrucke => e.expr()

exprmemberexprmember

AnrufermitgliedHauptsächlich

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

Notiere dass der expr Und callermember Methoden sind nicht ganz „richtig“.Und da sehen Sie eine Wiederholung von ein entsprechender Kommentar Diese Reflexion ist etwa 15x schneller als Stacktrace.

BEARBEITEN:MethodBase ist wahrscheinlich eine bessere Möglichkeit, nur die Methode abzurufen, in der Sie sich befinden (im Gegensatz zum gesamten Aufrufstapel).Ich würde mir jedoch immer noch Sorgen um das Inlining machen.

Sie können einen StackTrace innerhalb der Methode verwenden:

StackTrace st = new StackTrace(true);

Und der Blick auf die Rahmen:

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

Beachten Sie jedoch, dass Sie sich nicht in der Methode befinden, in der Sie sich befinden, wenn die Methode inline ist.Sie können ein Attribut verwenden, um Inlining zu verhindern:

[MethodImpl(MethodImplOptions.NoInlining)]

Der einfache Weg, damit umzugehen, ist:

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

Wenn die System.Reflection im using-Block enthalten ist:

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

Wie wäre es damit:

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

Ich denke, Sie sollten in der Lage sein, das durch die Erstellung eines zu erreichen StackTrace.Oder als @Rand Und @Lars Mæhlum erwähnen, MethodBase.GetCurrentMethod()

Wenn Sie nur den Zeichenfolgennamen der Methode benötigen, können Sie Ausdrücke verwenden.Sehen http://joelabrahamsson.com/entry/getting-property-and-method-names-using-static-reflection-in-c-sharp

Versuche dies...

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

Ich habe das gerade mit einer einfachen statischen Klasse gemacht:

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

dann in deinem 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();
        }
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top