현재 메소드를 호출 한 메소드를 어떻게 찾을 수 있습니까?


503

C #에 로그인 할 때 현재 메서드를 호출 한 메서드의 이름을 어떻게 알 수 있습니까? 에 대해 모두 알고 System.Reflection.MethodBase.GetCurrentMethod()있지만 스택 추적에서이 단계 아래로 한 단계 이동하고 싶습니다. 스택 추적을 파싱하는 것을 고려 Assembly.GetCallingAssembly()했지만 메소드 와 같은 더 명확한 방법을 찾고 싶습니다 .


22
.net 4.5 베타 +를 사용하는 경우 CallerInformation API를 사용할 수 있습니다 .
Rohit Sharma

5
발신자 정보도 많이 빠르게
비둘기

4
나는 세 가지 주요 방법 ( , 및 )에 대한 빠른 BenchmarkDotNet 벤치 마크를 작성하고 다른 사람들이 볼 수있는 결과를 gist.github.com/wilson0x4d/7b30c3913e74adf4ad99b09163a57a1fStackTraceStackFrameCallerMemberName
Shaun Wilson

답변:


512

이 시도:

using System.Diagnostics;
// Get call stack
StackTrace stackTrace = new StackTrace(); 
// Get calling method name
Console.WriteLine(stackTrace.GetFrame(1).GetMethod().Name);

짧막 한 농담:

(new System.Diagnostics.StackTrace()).GetFrame(1).GetMethod().Name

리플렉션 [C #]을 사용하여 호출 방법 가져 오기 입니다.


12
전체 스택이 아닌 필요한 프레임 만 만들 수도 있습니다.
Joel Coehoorn

187
새로운 StackFrame (1) .GetMethod (). Name;
Joel Coehoorn

12
그러나 완전히 신뢰할 수는 없습니다. 이것이 주석으로 작동하는지 봅시다! 콘솔 응용 프로그램에서 다음을 시도하면 컴파일러 최적화로 인해 오류가 발생합니다. static void Main (string [] args) {CallIt (); } private static void CallIt () {Final (); } static void Final () {StackTrace 추적 = 새 StackTrace (); StackFrame frame = trace.GetFrame (1); Console.WriteLine ( "{0}. {1} ()", frame.GetMethod (). DeclaringType.FullName, frame.GetMethod (). Name); }
BlackWasp 2016 년

10
컴파일러 인라인 또는 테일 콜이 메소드를 최적화 할 때 작동하지 않습니다.이 경우 스택이 축소되고 예상보다 다른 값을 찾을 수 있습니다. 디버그 빌드에서만 이것을 사용하면 잘 작동합니다.
Abel

46
과거에 한 일은 스택 트레이스를 찾는 메소드 앞에 컴파일러 속성 [MethodImplAttribute (MethodImplOptions.NoInlining)]을 추가 하는 것입니다. 그러면 컴파일러가 메소드를 인라인하지 않고 스택 추적에 실제 호출 메소드가 포함됩니다 (대부분의 경우 꼬리 재귀에 대해 걱정하지 않습니다)
Jordan Rieger

363

C # 5에서는 호출자 정보를 사용하여 해당 정보를 얻을 수 있습니다 .

//using System.Runtime.CompilerServices;
public void SendError(string Message, [CallerMemberName] string callerName = "") 
{ 
    Console.WriteLine(callerName + "called me."); 
} 

당신은 또한 얻을 수 있습니다 [CallerFilePath][CallerLineNumber].


13
안녕하세요, C # 5가 아니며 4.5에서 사용할 수 있습니다.
AFract

35
@AFract Language (C #) 버전은 .NET 버전과 다릅니다.
kwesolowski

6
@stuartd 같이 보인다는 [CallerTypeName]현재 .NET기구 (4.6.2) 및 코어 CLR에서 적하
Ph0en1x

4
Ph0en1x @는 프레임 워크에 결코 내 포인트는 예를 들어, 있다면 그것은 편리한 것이었다 CallerMember의 유형 이름을 얻는 방법
stuartd

3
@ DiegoDeberdt-컴파일 타임에 모든 작업을 수행하기 때문에 이것을 사용하면 반사 단점이 없다는 것을 읽었습니다. 나는 그것이 방법이라고 불리는 것에 대해 정확하다고 생각합니다.
cchamberlain

109

발신자 정보 및 선택적 매개 변수를 사용할 수 있습니다.

public static string WhoseThere([CallerMemberName] string memberName = "")
{
       return memberName;
}

이 테스트는 이것을 보여줍니다 :

[Test]
public void Should_get_name_of_calling_method()
{
    var methodName = CachingHelpers.WhoseThere();
    Assert.That(methodName, Is.EqualTo("Should_get_name_of_calling_method"));
}

StackTrace는 매우 빠르게 작동하지만 대부분의 경우 호출자 정보가 훨씬 빠릅니다. 1000 회 반복 샘플에서 40 배 빠르게 클럭했습니다.


.Net 4.5에서만 사용 가능
DerApe

1
호출자가 기본값을 대체 하기 때문에 호출자가 agrument : CachingHelpers.WhoseThere("wrong name!");==>를 전달하면 작동하지 않습니다 . "wrong name!"CallerMemberName
Olivier Jacot-Descombes

@ OlivierJacot-Descombes는 매개 변수를 전달하면 확장 방법이 작동하지 않는 것과 같은 방식으로 작동하지 않습니다. 다른 문자열 매개 변수를 사용할 수 있습니다. 또한 resharper는 당신이했던 것처럼 인수를 전달하려고하면 경고를 줄 것입니다.
비둘기

1
@dove 당신은 this확장 메소드에 명시 적 매개 변수를 전달할 수 있습니다 . 또한 Olivier는 정확하며 값을 전달할 수 있으며 [CallerMemberName]적용되지 않습니다. 대신 기본값이 일반적으로 사용되는 재정의 기능을합니다. 실제로 IL을 살펴보면 결과 방법이 일반적으로 [opt]arg에 대해 방출 된 것과 다르지 않다는 것을 알 수 CallerMemberName있으므로 주입은 CLR 동작입니다. 마지막으로, 문서 : "발신자 정보 속성 [...] 은 인수가 생략 될 때 전달되는 기본값에 영향을줍니다 "
Shaun Wilson

2
이것은 완벽하고 async친절하며 StackFrame도움이되지 않습니다. 람다에서 호출되는 데에도 영향을 미치지 않습니다.
Aaron

65

속도 비교가 중요한 부분 인 2 가지 방법에 대한 빠른 요약.

http://geekswithblogs.net/BlackRabbitCoder/archive/2013/07/25/c.net-little-wonders-getting-caller-information.aspx

컴파일 타임에 호출자 결정

static void Log(object message, 
[CallerMemberName] string memberName = "",
[CallerFilePath] string fileName = "",
[CallerLineNumber] int lineNumber = 0)
{
    // we'll just use a simple Console write for now    
    Console.WriteLine("{0}({1}):{2} - {3}", fileName, lineNumber, memberName, message);
}

스택을 사용하여 발신자 결정

static void Log(object message)
{
    // frame 1, true for source info
    StackFrame frame = new StackFrame(1, true);
    var method = frame.GetMethod();
    var fileName = frame.GetFileName();
    var lineNumber = frame.GetFileLineNumber();

    // we'll just use a simple Console write for now    
    Console.WriteLine("{0}({1}):{2} - {3}", fileName, lineNumber, method.Name, message);
}

두 가지 접근법의 비교

Time for 1,000,000 iterations with Attributes: 196 ms
Time for 1,000,000 iterations with StackTrace: 5096 ms

따라서 속성을 사용하는 것이 훨씬 빠릅니다. 실제로 거의 25 배 더 빠릅니다.


이 방법은 탁월한 접근 방법 인 것 같습니다. 네임 스페이스를 사용할 수 없다는 문제없이 Xamarin에서도 작동합니다.
lyndon hughey

63

우리는 전체 스택이 아니라 실제로 필요한 프레임 만 인스턴스화하여 Assad의 코드 (현재 허용되는 답변)를 약간 향상시킬 수 있습니다.

new StackFrame(1).GetMethod().Name;

이 방법은 조금 더 나은 성능을 발휘할 수 있지만 모든 가능성에서 여전히 단일 프레임을 만들려면 전체 스택을 사용해야합니다. 또한 Alex Lyman이 지적한 것과 동일한 경고가 여전히 있습니다 (최적화 / 기본 코드는 결과를 손상시킬 수 있음). 마지막으로, 가능성이없는 것처럼 확인 new StackFrame(1)하거나 .GetFrame(1)리턴하지 않는지 null확인할 수 있습니다.

이 관련 질문을 참조하십시오 : 리플렉션을 사용하여 현재 실행중인 메소드의 이름을 찾을 수 있습니까?


1
new ClassName(…)null 과 같은 것이 가능 합니까?
표시 이름

1
좋은 점은 이것이 .NET Standard 2.0에서도 작동한다는 것입니다.
srsedate

60

일반적으로 System.Diagnostics.StackTrace클래스를 사용 하여을 얻은 System.Diagnostics.StackFrame다음 GetMethod()메소드를 사용하여 System.Reflection.MethodBase객체 를 얻을 수 있습니다. 그러나이 방법에는 몇 가지주의 사항 이 있습니다.

  1. 런타임 스택을 나타냅니다. 최적화는 메소드를 인라인 할 수 있으며 스택 추적에서 해당 메소드를 볼 수 없습니다 .
  2. 기본 프레임 은 표시 되지 않으므로 기본 메소드에서 메소드를 호출 할 가능성이 있어도 작동 하지 않으며 실제로 사용할 수있는 방법이 없습니다.

( 참고 : Firas Assad가 제공 한 답변을 확장하고 있습니다.)


2
최적화가 해제 된 디버그 모드에서 스택 추적에 메소드가 무엇인지 볼 수 있습니까?
AttackingHobo

1
@AttackingHobo : 예. 메소드가 인라인되거나 (최적화) 기본 프레임이 아니라면 볼 수 있습니다.
Alex Lyman

38

.NET 4.5부터 발신자 정보 속성을 사용할 수 있습니다 .

  • CallerFilePath -함수를 호출 한 소스 파일
  • CallerLineNumber -함수를 호출 한 코드 라인;
  • CallerMemberName -함수를 호출 한 멤버

    public void WriteLine(
        [CallerFilePath] string callerFilePath = "", 
        [CallerLineNumber] long callerLineNumber = 0,
        [CallerMemberName] string callerMember= "")
    {
        Debug.WriteLine(
            "Caller File Path: {0}, Caller Line Number: {1}, Caller Member: {2}", 
            callerFilePath,
            callerLineNumber,
            callerMember);
    }

 

이 기능은 ".NET Core"및 ".NET Standard"에도 있습니다.

참고 문헌

  1. Microsoft-발신자 정보 (C #)
  2. Microsoft- CallerFilePathAttribute클래스
  3. Microsoft- CallerLineNumberAttribute클래스
  4. Microsoft- CallerMemberNameAttribute클래스

15

최적화로 인해 릴리스 코드에서는 그렇게 할 수 없습니다. 또한 샌드 박스 모드 (네트워크 공유)에서 응용 프로그램을 실행하면 스택 프레임을 전혀 가져올 수 없습니다.

PostSharp 와 같은 AOP ( Aspect-Oriented Programming)를 고려하십시오. PostSharp 는 코드에서 호출되는 대신 코드를 수정하여 항상 위치를 알고 있습니다.


이것이 릴리스에서 작동하지 않는다는 것이 절대적으로 맞습니다. 코드 삽입이라는 아이디어가 마음에 들지 않지만 디버그 문에는 코드 수정이 필요하지만 여전히 그렇습니다. C 매크로로 돌아가는 것이 어떻습니까? 적어도 당신이 볼 수있는 것입니다.
ebyrob 2016 년

9

분명히 이것은 늦은 답변이지만 .NET 4.5 이상을 사용할 수 있다면 더 나은 옵션이 있습니다.

internal static void WriteInformation<T>(string text, [CallerMemberName]string method = "")
{
    Console.WriteLine(DateTime.Now.ToString() + " => " + typeof(T).FullName + "." + method + ": " + text);
}

현재 날짜와 시간 뒤에 "Namespace.ClassName.MethodName"이 붙고 ": text"로 끝납니다.
샘플 출력 :

6/17/2016 12:41:49 PM => WpfApplication.MainWindow..ctor: MainWindow initialized

샘플 사용 :

Logger.WriteInformation<MainWindow>("MainWindow initialized");

8
/// <summary>
/// Returns the call that occurred just before the "GetCallingMethod".
/// </summary>
public static string GetCallingMethod()
{
   return GetCallingMethod("GetCallingMethod");
}

/// <summary>
/// Returns the call that occurred just before the the method specified.
/// </summary>
/// <param name="MethodAfter">The named method to see what happened just before it was called. (case sensitive)</param>
/// <returns>The method name.</returns>
public static string GetCallingMethod(string MethodAfter)
{
   string str = "";
   try
   {
      StackTrace st = new StackTrace();
      StackFrame[] frames = st.GetFrames();
      for (int i = 0; i < st.FrameCount - 1; i++)
      {
         if (frames[i].GetMethod().Name.Equals(MethodAfter))
         {
            if (!frames[i + 1].GetMethod().Name.Equals(MethodAfter)) // ignores overloaded methods.
            {
               str = frames[i + 1].GetMethod().ReflectedType.FullName + "." + frames[i + 1].GetMethod().Name;
               break;
            }
         }
      }
   }
   catch (Exception) { ; }
   return str;
}

죄송합니다. "MethodAfter"매개 변수를 조금 더 잘 설명해야합니다. 따라서 "log"유형 함수에서이 메소드를 호출하는 경우 "log"함수 바로 다음에 메소드를 가져 오려고합니다. GetCallingMethod ( "log")를 호출합니다.
플랜더스

6

아마도 당신은 다음과 같은 것을 찾고있을 것입니다 :

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

4
private static MethodBase GetCallingMethod()
{
  return new StackFrame(2, false).GetMethod();
}

private static Type GetCallingType()
{
  return new StackFrame(2, false).GetMethod().DeclaringType;
}

환상적인 수업은 여기에 있습니다 : http://www.csharp411.com/c-get-calling-method/


StackFrame이 신뢰할 수 없습니다. "2 프레임"을 올리면 메소드 호출도 쉽게 되돌아 갈 수 있습니다.
user2864740

2

내가 사용한 또 다른 접근법은 문제의 메소드에 매개 변수를 추가하는 것입니다. 예를 들어 대신을 void Foo()사용하십시오 void Foo(string context). 그런 다음 호출 컨텍스트를 나타내는 고유 한 문자열을 전달하십시오.

개발을 위해 발신자 / 문맥 만 필요한 경우 param선적 전 제거 할 수 있습니다 .


2

메소드 이름과 클래스 이름을 얻으려면 다음을 시도하십시오.

    public static void Call()
    {
        StackTrace stackTrace = new StackTrace();

        var methodName = stackTrace.GetFrame(1).GetMethod();
        var className = methodName.DeclaringType.Name.ToString();

        Console.WriteLine(methodName.Name + "*****" + className );
    }

1
StackFrame caller = (new System.Diagnostics.StackTrace()).GetFrame(1);
string methodName = caller.GetMethod().Name;

충분하다고 생각합니다.



1

발신자를 찾기 위해 람다를 사용할 수도 있습니다.

자신이 정의한 메소드가 있다고 가정하십시오.

public void MethodA()
    {
        /*
         * Method code here
         */
    }

발신자 찾기를 원합니다.

1 . 메소드 유형의 매개 변수를 갖도록 메소드 서명을 변경하십시오 (Func도 작동 함).

public void MethodA(Action helperAction)
        {
            /*
             * Method code here
             */
        }

2 . 람다 이름은 임의로 생성되지 않습니다. 규칙은 다음과 같습니다.> <CallerMethodName> __ X 여기서 CallerMethodName은 이전 함수로 대체되고 X는 인덱스입니다.

private MethodInfo GetCallingMethodInfo(string funcName)
    {
        return GetType().GetMethod(
              funcName.Substring(1,
                                funcName.IndexOf("&gt;", 1, StringComparison.Ordinal) - 1)
              );
    }

3 . MethodA를 호출 할 때 Action / Func 매개 변수는 호출자 메소드에 의해 생성되어야합니다. 예:

MethodA(() => {});

4 . MethodA 내부에서 이제 위에서 정의한 도우미 함수를 호출하고 호출자 메서드의 MethodInfo를 찾을 수 있습니다.

예:

MethodInfo callingMethodInfo = GetCallingMethodInfo(serverCall.Method.Name);

0

Firas Assaad 답변에 대한 추가 정보.

new StackFrame(1).GetMethod().Name;.net core 2.1에서 의존성 주입 을 사용 했으며 메소드를 '시작'으로 호출하고 있습니다.

나는 시도하고 [System.Runtime.CompilerServices.CallerMemberName] string callerName = "" 올바른 호출 방법을 제공합니다


-1
var callingMethod = new StackFrame(1, true).GetMethod();
string source = callingMethod.ReflectedType.FullName + ": " + callingMethod.Name;

1
나는 downvote하지 않았지만 왜 당신이 매우 유사한 정보를 게시했는지 설명하기 위해 몇 년 후 텍스트를 추가하면 질문의 가치가 높아지고 더 많은 downvoting을 피할 수 있습니다.
Shaun Wilson
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.