[C#] CallerMemberAttribute 를 이용한 현재 메소드의 호출자 정보 알아오기 및 성능 비교 (feat. StackTrace)
C#을 이용해 개발 중이라면, 간혹 실행중인 코드에 대한 호출자정보를 찾아야 할 경우가 있습니다.
Method 이름, 실행중인 파일, 또는 행 번호와 같은 구체적인 호출자정보 일 수 있습니다.
저는 주로 이런한 내용이 필요했던 경우는
실행중인 프로그램내에서 발생한 Log를 남길 경우, 어느 파일, 어느 Method 에서 발생한 오류인지를 남기 위해서 필요했습니다. 좀 더 빠른 파악이 가능하고, 체계화된 프로세스를 이용중이라면 빠른 디버깅이 가능하기 때문입니다.
이를 위해서 호출자정보를 찾던 도중 .NET Framework 4.5에 추가된 CallerMemberAttribute 에 대해서 알게 되었습니다.
기존에는 Reflection 을 이용해서 처리 하였지만, Reflection이 관련된 경우 현재 코드에 많은 양의 메타 데이터가 로드 및 처리되어야 하므로 성능이 중요한 문제가 될 수 있다는 글을 다수 발견 하였습니다.
Microsoft MVP 인 Rick Strahl의 게시물에 따르면, 처리 시간이 약 4배 느려질수 있다고 합니다. (일부의 경우)
https://weblog.west-wind.com/posts/2004/Apr/11/Net-Reflection-and-Performance
Reflection 을 이용한 처리
Common.Util.cs
public void GetMethod()
{
SetLog(System.Reflection.MethodBase.GetCurrentMethod(), "Error xxxxxx")
}
public void SetLog( MethodBase method, string msg )
{
NPLog.PrintError( $"{method.ReflectedType.FullName}.{method.Name} - {msg}" );
}
// 실행결과
Common.Util.GetMethod() - Error xxxxxxxxxxx
StackTrace 를 이용한 처리
Common.Util.cs
public void GetMethod()
{
SetLog(new StackTrace().GetFrame(1).GetMethod(), "Error xxxxxx");
}
public void SetLog( MethodBase method, string msg )
{
NPLog.PrintError( $"{method.ReflectedType.FullName}.{method.Name} - {msg}" );
}
// 실행결과
Common.Util.GetMethod() - Error xxxxxxxxxxx
CallerMemberAttribute 를 이용한 처리
Common.Util.cs
public void GetMethod()
{
SetLog("Error xxxxxx");
}
public void SetLog( string message, [CallerFilePath] string filePath = null,
[CallerMemberName] string method = null,
[CallerLineNumber] int lineNumber = 0 )
{
NLog.PrintError( $"{filePath} ({method} line is {lineNumber}) - " + message );
}
// 실행결과
D:\Project\SampleProject\Common\Util.cs (GetMethod()) line is 90 - Error xxxxx
속도 차이
참조 : http://rion.io/2017/11/18/knowing-when-to-reflect-with-caller-info-attributes/
위 링크를 인용하였지만, 1백만 이상 반복한 결과 이와 같은 속도 차이가 있다고 합니다.
또한 CallerMemberName은 메모리 측면에서 훨씬 더 효율적일뿐만 아니라 다른 두 옵션 중 하나보다 훨씬 빠릅니다.
추가
CallerMemberAttribute 를 사용하면, 속도면에서는 우수 하지만, Log로 남기고 보여지는 부분에서 약간의 가공을 하여야 보기가 좋습니다.
간단한 파일 구조를 가지고 있다면 모르겠지만, 조금 세분화된 네임스페이스와 파일 구조를 가지고 있다면, 아래와 같이 가공을 하면 조금더 FilePath를 간략해 해서 볼수 있습니다.
public void SetLog( string message, [CallerFilePath] string filePath = null,
[CallerMemberName] string method = null,
[CallerLineNumber] int lineNumber = 0 )
{
NLog.PrintError( $"{filePath.Substring( filePath.IndexOf( System.Reflection.Assembly.GetExecutingAssembly().GetName().Name ) )} ({method} line is {lineNumber}) - " + message );
}
Common.Util - Error xxxxxx
'Program Language > C#' 카테고리의 다른 글
[C#] SonarQube - 정적분석 (0) | 2020.01.29 |
---|---|
[C#] MD5를 이용해서 암호화 방법 (0) | 2020.01.13 |
[C#] Enum 값 검사 (0) | 2019.12.26 |
[C#] 인터페이스를 이용한 콜백 (0) | 2019.12.17 |
[C#] 문자열 숫자형식 포맷팅 String.Format ( C# Numberic Formatting ) (0) | 2013.01.10 |