리플렉션을 사용하여 현재 실행중인 메소드의 이름을 찾을 수 있습니까?


202

제목처럼 : 리플렉션은 현재 실행중인 메소드의 이름을 알려줍니다.

나는 Heisenberg 문제 때문에 추측하지 않는 경향이 있습니다. 현재 메소드를 변경하지 않고 현재 메소드를 알려주는 메소드를 어떻게 호출합니까? 그러나 누군가가 나를 잘못 증명할 수 있기를 바랍니다.

최신 정보:

  • 2 부 : 속성의 코드 내부를 살펴볼 수 있습니까?
  • 3 부 : 성능은 어떻습니까?

최종 결과
MethodBase.GetCurrentMethod ()에 대해 배웠습니다. 또한 스택 추적을 만들 수있을뿐만 아니라 원하는 경우 필요한 정확한 프레임 만 만들 수 있다는 것도 알게되었습니다.

속성 내에서 이것을 사용하려면 .Substring (4)를 사용하여 'set_'또는 'get_'을 제거하십시오.


Joel, 나는 오래된 질문을 알고 있지만 방법의 정확한 프레임을 생성한다는 것은 무엇을 의미합니까?
Abhijeet

호출 스택의 특정 항목, 즉 중요한 스택 추적 부분을 나타냅니다.
Joel Coehoorn

답변:


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)지터가 인라인을 시작하기로 결정할 때까지는 로깅에 대한 다른 답변에 설명 된 방법 을 사용하고있었습니다 . 성능상의 이유로 인라인을 방지하기 위해 속성을 추가하고 싶지 않았습니다. 이 [CallerMemberName]접근 방식을 사용하면 문제가 해결되었습니다. 감사!
Brian Rogers

5
[CallerMemberName]은 (는) BCL 빌드로 포장 된
Venson

2
디버그 모드에서 StackFrame (1)을 사용하면 작동한다는 점을 고려하십시오. 그러나 컴파일 할 때 릴리스 모드를 사용하는 경우 일부 최적화가있을 수 있으며 스택이 예상과 다를 수 있습니다.
Axel O'Connell 11:27에

현재 실행중인 메소드 대신 호출 멤버 (예 : SomeProperty)를 반환하지 않습니까?
Lennart

1
예, 세터를 호출하기에 호출됩니다 OnPropertyChanged("SomeProperty")하지OnPropertyChanged("SetProperty")
존 닐슨

189

async방법이 아닌 경우 사용할 수 있습니다

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

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

async메소드의 경우 "MoveNext"가 리턴 됨을 기억하십시오 .


8
이것이 항상 예상되는 결과를 낳지는 않는다는 점에 유의하십시오. 즉, 작은 메서드 나 속성은 종종 릴리스 빌드에서 인라인됩니다.이 경우 결과는 대신 호출자의 메서드 이름이됩니다.
Abel

5
내가 아는 한 런타임에 MSIL은 더 이상 실행 포인터에서 사용할 수 없으므로 JITted입니다. 메소드 이름을 알고 있으면 리플렉션을 계속 사용할 수 있습니다. 요점은 인라인 상태 일 때 현재 실행중인 메소드가 이제 다른 메소드 (즉, 스택에서 하나 이상의 상위)입니다. 즉, 방법이 사라졌습니다. NoInlining으로 분석법을 표시하더라도 꼬리 호출이 최적화 될 가능성이 여전히 있습니다.이 경우에도 사라집니다. 그러나 디버그 빌드 중에는 작동합니다.
Abel

1
인라인을 피하려면 메소드 위에 [MethodImpl (MethodImplOptions.NoInlining)] 속성을 추가하십시오.
alex.peter

내부 async메소드에는 메소드 이름으로 "MoveNext"가 표시 될 것입니다.
빅터 Yarema

46

Lex가 제공 한 스 니펫은 약간 길어서 다른 사람이 정확히 동일한 기술을 사용하지 않았기 때문에 중요한 부분을 지적하고 있습니다.

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

이것은 동일한 결과를 MethodBase.GetCurrentMethod (). Name 기술에 반환해야 하지만 이전 방법에 대해 색인 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의 답변을 참조하십시오.
Brian Rogers

이것은 허용되는 답변보다 낫고 위의 답변도
좋습니다.

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

답신이 늦었지만 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하면 스택 추적이 파일 이름, 행 번호 등을 캡처하려고 시도하므로이 호출이 느려질 수 있습니다. 그렇지 않으면, 좋은 대답
Ivaylo Slavov

6

처리하는 간단한 방법은 다음과 같습니다.

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

using 블록에 System.Reflection이 포함 된 경우 :

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

-1
new StackTrace().ToString().Split("\r\n",StringSplitOptions.RemoveEmptyEntries)[0].Replace("at ","").Trim()
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.