현재 메서드를 호출한 메서드를 어떻게 찾을 수 있나요?
-
05-07-2019 - |
문제
C#에 로그인할 때 현재 메서드를 호출한 메서드 이름을 어떻게 알 수 있나요?나는 모든 것을 알고 있다 System.Reflection.MethodBase.GetCurrentMethod()
, 하지만 스택 추적에서 이보다 한 단계 아래로 이동하고 싶습니다.스택 추적을 구문 분석하는 것을 고려했지만 다음과 같이 더 명확하고 명시적인 방법을 찾고 싶습니다. Assembly.GetCallingAssembly()
하지만 방법의 경우.
해결책
이 시도:
using System.Diagnostics;
// Get call stack
StackTrace stackTrace = new StackTrace();
// Get calling method name
Console.WriteLine(stackTrace.GetFrame(1).GetMethod().Name);
그것은 왔습니다 반사를 사용하여 통화 방법을 얻으십시오 [C#.
다른 팁
C# 5에서는 발신자 정보를 사용하여 해당 정보를 얻을 수 있습니다.
//using System.Runtime.CompilerServices;
public void SendError(string Message, [CallerMemberName] string callerName = "")
{
Console.WriteLine(callerName + "called me.");
}
당신은 또한 얻을 수 있습니다 [CallerFilePath]
그리고 [CallerLineNumber]
.
발신자 정보 및 선택적 매개 변수를 사용할 수 있습니다.
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 배 더 빠르게 시계를 기록했다.
전체 스택이 아닌 실제로 필요한 프레임만 인스턴스화하여 Mr Assad의 코드(현재 허용되는 답변)를 약간 개선할 수 있습니다.
new StackFrame(1).GetMethod().Name;
이는 성능이 조금 더 좋을 수 있지만 단일 프레임을 생성하려면 여전히 전체 스택을 사용해야 합니다.또한 Alex Lyman이 지적한 것과 동일한 주의 사항이 있습니다(최적화 프로그램/네이티브 코드가 결과를 손상시킬 수 있음).마지막으로 다음 사항을 확인하고 싶을 수도 있습니다. new StackFrame(1)
또는 .GetFrame(1)
돌아오지 마세요 null
, 그럴 가능성은 거의 없을 것 같습니다.
다음 관련 질문을 참조하세요.리플렉션을 사용하여 현재 실행 중인 메서드의 이름을 찾을 수 있나요?
일반적으로 사용할 수 있습니다 System.Diagnostics.StackTrace
수업을 얻습니다 System.Diagnostics.StackFrame
, 다음을 사용합니다 GetMethod()
얻는 방법 System.Reflection.MethodBase
물체. 그러나 있습니다 일부 경고 이 접근법 :
- 그것은 실행 시간 스택 - 최적화는 메소드를 인화 할 수 있으며 ~ 아니다 스택 추적에서 해당 메소드를 참조하십시오.
- 그것은 할 것입니다 ~ 아니다 기본 프레임을 표시하므로 기본 방법으로 메소드를 호출 할 가능성이 있으면 ~ 아니다 작업하면 현재 사용 가능한 방법이 없습니다.
(참고 : 나는 단지 확장하고 있습니다 대답 Firas Assad가 제공합니다.)
속도 비교가 중요한 부분 인 2 가지 접근법의 빠른 요약.
컴파일 타임에서 발신자 결정
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 배 더 빠릅니다.
.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"에도 있습니다.
참조
최적화로 인해 릴리스 코드에서는 신뢰할 수 없습니다. 또한 샌드 박스 모드 (네트워크 공유)에서 응용 프로그램을 실행하면 스택 프레임을 전혀 잡을 수 없습니다.
고려하다 측면 지향 프로그래밍 (AOP), 좋아요 포스트 쇼트, 코드에서 호출되는 대신 코드를 수정하여 코드가 항상 어디에 있는지 알고 있습니다.
/// <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;
}
분명히 이것은 늦은 답변이지만 .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");
어쩌면 당신은 다음과 같은 것을 찾고있을 것입니다.
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
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/
내가 사용한 또 다른 접근법은 해당 메소드에 매개 변수를 추가하는 것입니다. 예를 들어, 대신 void Foo()
, 사용 void Foo(string context)
. 그런 다음 호출 컨텍스트를 나타내는 고유 한 문자열을 전달하십시오.
개발을 위해 발신자/컨텍스트 만 필요하면 param
배송 전에.
StackFrame caller = (new System.Diagnostics.StackTrace()).GetFrame(1);
string methodName = caller.GetMethod().Name;
충분할 것입니다.
보세요 .NET의 로깅 메소드 이름입니다. 생산 코드에서 사용하는 것을 조심하십시오. stackframe은 신뢰할 수 없을 수 있습니다 ...
발신자를 찾기 위해 Lambda를 사용할 수도 있습니다.
귀하가 정의한 방법이 있다고 가정합니다.
public void MethodA()
{
/*
* Method code here
*/
}
그리고 당신은 그것이 발신자라는 것을 찾고 싶습니다.
1. 메소드 서명을 변경하여 유형 액션의 매개 변수를 갖습니다 (FUNC도 작동 함).
public void MethodA(Action helperAction)
{
/*
* Method code here
*/
}
2. 람다 이름은 무작위로 생성되지 않습니다. 규칙은 다음과 같습니다.>u003CCallerMethodName> __x는 CallermethodName이 이전 함수로 대체되고 X는 인덱스입니다.
private MethodInfo GetCallingMethodInfo(string funcName)
{
return GetType().GetMethod(
funcName.Substring(1,
funcName.IndexOf(">", 1, StringComparison.Ordinal) - 1)
);
}
3. Methoda를 호출하면 action/func 매개 변수를 발신자 메소드에 의해 생성해야합니다. 예시:
MethodA(() => {});
4. 내부 메소드는 이제 위에서 정의 된 도우미 기능을 호출하고 발신자 메소드의 메소드를 찾을 수 있습니다.
예시:
MethodInfo callingMethodInfo = GetCallingMethodInfo(serverCall.Method.Name);
메소드 이름과 클래스 이름을 얻으려면 다음을 시도하십시오.
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 );
}
Firas Assaad 답변에 대한 추가 정보.
나는 사용했다 new StackFrame(1).GetMethod().Name;
의존성 주입이있는 .NET Core 2.1에서 '시작'으로 호출 메소드를 받고 있습니다.
나는 함께 시도했다 [System.Runtime.CompilerServices.CallerMemberName] string callerName = ""
그리고 그것은 올바른 호출 방법을 제공합니다
var callingMethod = new StackFrame(1, true).GetMethod();
string source = callingMethod.ReflectedType.FullName + ": " + callingMethod.Name;