호출자 정보
호출자 정보 (Caller Information)
F#에서 메서드를 호출한 쪽("호출자")이 누구였는지 추적해야 할 때가 있어요. 어느 소스 파일의 몇 번째 줄에서 호출했는지, 호출한 멤버 이름이 뭔지 같은 정보가 필요할 때 Caller Info 특성을 사용하면 손쉽게 알아낼 수 있죠. 이 글에서는 그 사용법을 실제 예제와 함께 하나씩 풀어볼게요.
출처: https://learn.microsoft.com/en-us/dotnet/fsharp/language-reference/caller-information
본문
Caller Info 특성(Caller Info attributes)을 사용하면 메서드가 호출된 시점의 정보를 얻을 수 있어요. 구체적으로는 소스 코드의 파일 경로, 소스 코드의 줄 번호, 그리고 호출한 멤버의 이름을 얻을 수 있어요. 이런 정보는 로그를 남기는 추적(tracing), 디버깅, 그리고 진단 도구(diagnostic tools)를 만들 때 특히 유용하죠.
이 정보를 얻으려면 각각 기본값이 있는 **선택적 매개 변수(optional parameters)**에 특성을 적용하면 돼요. 아래 표는 System.Runtime.CompilerServices 네임스페이스에 정의된 Caller Info 특성들을 정리한 거예요.
| 특성 | 설명 | 타입 |
|---|---|---|
CallerFilePath |
호출자가 포함된 소스 파일의 전체 경로. 컴파일 시점의 파일 경로예요. | String |
CallerLineNumber |
메서드가 호출된 소스 파일의 줄 번호. | Integer |
CallerMemberName |
호출자의 메서드 또는 속성 이름. 이 주제의 '멤버 이름' 절을 참고하세요. | String |
예제 (Example)
아래 예제는 이 특성들을 사용해 호출자를 추적하는 방법을 보여줘요.
open System.Diagnostics
open System.Runtime.CompilerServices
open System.Runtime.InteropServices
type Tracer() =
member _.DoTrace(message: string,
[<CallerMemberName; Optional; DefaultParameterValue("")>] memberName: string,
[<CallerFilePath; Optional; DefaultParameterValue("")>] path: string,
[<CallerLineNumber; Optional; DefaultParameterValue(0)>] line: int) =
Trace.WriteLine(sprintf $"Message: {message}")
Trace.WriteLine(sprintf $"Member name: {memberName}")
Trace.WriteLine(sprintf $"Source file path: {path}")
Trace.WriteLine(sprintf $"Source line number: {line}")
설명 (Remarks)
Caller Info 특성은 선택적 매개 변수에만 적용할 수 있어요. 이 특성이 붙으면 컴파일러가 해당 선택적 매개 변수에 알맞은 값을 대신 채워 넣어 줘요.
그 값은 컴파일 시점에 리터럴(literal)로 중간 언어(IL, Intermediate Language)에 기록돼요. 그래서 예외의 StackTrace 속성 결과와 달리 난독화(obfuscation)의 영향을 받지 않아요.
원한다면 선택적 인자를 직접 명시해서 호출자 정보를 직접 제어하거나, 반대로 호출자 정보를 숨길 수도 있어요.
멤버 이름 (Member names)
CallerMemberName 특성을 쓰면 호출되는 메서드에 멤버 이름을 String 인자로 일일이 지정하지 않아도 돼요. 이 방식의 장점은 이름 바꾸기 리팩터링(Rename Refactoring)을 해도 String 값이 안 바뀌는 문제를 피할 수 있다는 거예요. 특히 다음 작업에서 유용하죠.
- 추적·진단 루틴을 사용할 때.
- 데이터 바인딩 시
INotifyPropertyChanged인터페이스를 구현할 때. 이 인터페이스는 객체의 속성이 바뀌었을 때 바인딩된 컨트롤에 알려서 최신 정보를 표시하도록 해주는 역할을 해요.CallerMemberName특성이 없으면 속성 이름을 리터럴로 직접 지정해야 하죠.
아래 표는 CallerMemberName 특성을 사용했을 때 반환되는 멤버 이름을 정리한 거예요.
| 호출이 발생한 곳 | 멤버 이름 결과 |
|---|---|
| 메서드, 속성 또는 이벤트 | 호출이 시작된 메서드·속성·이벤트의 이름 |
| 생성자 | 문자열 ".ctor" |
| 정적 생성자 | 문자열 ".cctor" |
| 소멸자 | 문자열 "Finalize" |
| 사용자 정의 연산자 또는 변환 | 생성된 멤버 이름. 예: "op_Addition" |
| 특성 생성자 | 특성이 적용된 멤버의 이름. 특성이 멤버 내부의 요소(매개 변수, 반환 값, 제네릭 형식 매개 변수 등)에 적용되면 그 요소와 연결된 멤버의 이름이 돼요. |
| 포함하는 멤버가 없는 경우(예: 어셈블리 수준 또는 형식에 적용된 특성) | 선택적 매개 변수의 기본값 |