リフレクションを使用して、現在実行中のメソッドの名前を見つけることができますか?


202

タイトルのように:リフレクションは現在実行中のメソッドの名前を与えることができます。

ハイゼンベルク問題のため、私はそうは思わない傾向があります。現在のメソッドを変更せずに現在のメソッドを通知するメソッドをどのように呼び出しますか?しかし、私は誰かが私がそこで間違っていることを証明できることを望んでいます。

更新:

  • パート2:これを使用して、プロパティのコード内を調べることもできますか?
  • パート3:パフォーマンスはどのようになりますか?

最終結果
MethodBase.GetCurrentMethod()について学びました。また、スタックトレースを作成できるだけでなく、必要に応じて必要なフレームのみを作成できることも学びました。

これをプロパティ内で使用するには、.Substring(4)を使用して「set_」または「get_」を削除します。


ジョエル、私は古い質問を知っていますが、メソッドの正確なフレームを作成することはどういう意味ですか?
Abhijeet 2013

これは、コールスタック内の特定の項目(スタックトレースの重要な部分)を指します。
Joel Coehoorn 2013

回答:


119

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

コンパイラーは、コールサイトで一致する文字列リテラルを提供するため、基本的にパフォーマンスのオーバーヘッドはありません。


3
これは素晴らしい!私はStackFrame(1)、他の回答で説明されているロギングの方法を使用していました。これは、Jitterがインライン化を開始することを決定するまで機能するようでした。パフォーマンス上の理由からインライン化を防ぐために属性を追加したくありませんでした。[CallerMemberName]アプローチを使用して問題を修正しました。ありがとう!
ブライアンロジャース

5
[CallerMemberName]は、BCLビルドがパック
Venson 2013

2
StackFrame(1)をデバッグモードで使用すると機能することを考慮してください。ただし、コンパイル時にリリースモードを使用すると、いくつかの最適化が行われ、スタックが期待どおりにならない場合があります。
Axel O'Connell

これは、現在実行中のメソッドではなく、呼び出し元のメンバー(SomePropertyなど)を返しませんか?
Lennart

1
はい、セッターを呼び出すと、呼び出しが行われ、呼び出しは行われOnPropertyChanged("SomeProperty")ませんOnPropertyChanged("SetProperty")
John Nilsson

189

asyncメソッドの場合、使用できる

System.Reflection.MethodBase.GetCurrentMethod().Name;

https://docs.microsoft.com/en-us/dotnet/api/system.reflection.methodbase.getcurrentmethod

asyncメソッドの場合は「MoveNext」を返すことに注意してください。


8
これが常に期待した結果をもたらすとは限らないことに注意してください。つまり、小さなメソッドやプロパティは、リリースビルドでインライン化されることが多く、その場合、結果は代わりに呼び出し元のメソッド名になります。
アベル

5
私の知る限り、いいえ。ランタイムでは、MSILは実行ポインタから利用できなくなります(JITされます)。メソッドの名前がわかっていれば、リフレクションを使用できます。ポイントは、インライン化すると、現在実行中のメソッドが別のメソッドになっている(つまり、スタックの1つ以上上位)ことです。つまり、メソッドが消えました。メソッドをNoInliningでマークしたとしても、末尾呼び出しが最適化される可能性はありますが、その場合はそれもなくなります。ただし、デバッグビルド中は機能します。
アベル

1
インラインを回避するには、メソッドの上に[MethodImpl(MethodImplOptions.NoInlining)]属性を追加します。
alex.peter 2018

asyncメソッド内では、メソッド名として「MoveNext」を取得する可能性が最も高くなります。
Victor Yarema

46

Lexによって提供されたスニペットは少し長いため、他の誰もまったく同じ手法を使用していないため、重要な部分を指摘しています。

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

これは、MethodBase.GetCurrentMethod()。Nameテクニックと同じ結果を返すはずですが、前のメソッドのインデックス1を使用して独自のメソッドで一度実装し、さまざまなプロパティから呼び出すことができるため、指摘する価値があります。また、スタックトレース全体ではなく、1つのフレームのみを返します。

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

それもワンライナーです;)


ヘルパークラスのパブリック静的文字列GetPropertyName()を使用できますか?静的メソッド?
Kiquenet

2
Ed Guinessの回答と同じ:スタックはリリースビルドで異なる可能性があり、インライン化または末尾呼び出しの最適化の場合、最初のメソッドは現在のメソッドと同じでない場合があります。
Abel

.Net 4.5を使用している場合のインライン化の問題を回避する方法については、John Nilssonの回答を参照してください。
ブライアンロジャース

これは、受け入れられた回答および上記の回答よりも優れている可能性があります
T.Todua '27

16

空のコンソールプログラムのMainメソッド内でこれを試してください。

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

コンソール出力:
Main


12

はい、間違いなく。

オブジェクトを操作したい場合は、次のような関数を実際に使用します。

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サポートの解決策のようでした。


ばかげていません。私は単にそれらを渡しました。おそらく、見やすくするために何かを行うことができますが、比率に報いる努力は、それを単純に保つことを支持するようでした。基本的に、開発者はメソッドシグネチャのパラメータリストにコピーするだけです(もちろん、タイプを削除します)。
Lex

それは何ですか:ExceptionFormatとGetParameterList?
Kiquenet、2011

返答はかなり遅いですが、ExceptionFormatは定数文字列形式で、GetParameterListはパラメータを値でフォーマットする単純な関数です(これはインラインで実行できます)
Lex

11

メソッド名を取得する方法の比較- 任意のタイミング構造を使用 -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 /programming/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;
    }
}

結果

反射 反射

スタック トレーススタックトレース

インライン 定数インライン定数

一定 一定

expr e => e.expr()

exprmember exprmember

発信者 メイン

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

exprcallermemberメソッドは完全に「適切」ではないことに注意してください。また、リフレクションがスタックトレースよりも最大15倍速いという関連コメントの繰り返しが表示されます。


9

編集: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)]

リリースの最適化によるインラインは、コードがデバッグ構成とリリース構成で異なる動作をするため、特にトリッキーです。小さな物件に気をつけてください、彼らはこれの最もありそうな犠牲者です。
DK。

なぜのnew StackTrace(true)代わりにwoudを使うのかしらnew StackTrace(false)。これをに設定trueすると、スタックトレースがファイル名、行番号などをキャプチャしようとするため、この呼び出しが遅くなる可能性があります。それ以外の場合、良い答え
イヴァイロスラボフ

6

対処する簡単な方法は次のとおりです。

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

System.Reflectionがusingブロックに含まれている場合:

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

4

これはどうですか:

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


0

これを試して...

    /// <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

私は単純な静的クラスでこれを実行しました:

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

あなたのコードで:

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

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.