[C#]Getting Caller Method Name, File Path, and Line Number

目次

Overview

This implementation allows you to identify “where” and “by whom” a method was called within your program. By using .NET’s standard Caller Information Attributes, you can automatically receive source code information about the caller as arguments, which is useful for logging and debugging.

Specifications (Input/Output)

  • Input: Strings or integers defined as optional arguments of a method. The caller does not need to explicitly provide these arguments; the compiler automatically injects the values.
  • Output:
    • CallerFilePath: The full path of the caller’s source file (string).
    • CallerLineNumber: The line number of the caller (int).
    • CallerMemberName: The name of the caller’s method or property (string).
  • Prerequisites:
    • The System.Runtime.CompilerServices namespace is required.
    • Target arguments must have default values (e.g., = "" or = 0).

Basic Usage

To use this feature, simply add the attributes to the method arguments. The caller calls the method as usual.

// Definition side
public void Log(string message, 
    [CallerMemberName] string member = "", 
    [CallerLineNumber] int line = 0)
{
    Console.WriteLine($"{member}({line}): {message}");
}

// Caller side
Log("Error occurred"); 
// Output example: Main(15): Error occurred

Full Code

The following is a console application that defines a custom logger class to output logs with caller information.

using System;
using System.Runtime.CompilerServices;
using System.IO;

class Program
{
    static void Main()
    {
        // 1. Normal method call
        // The compiler automatically inserts file paths and other info without explicit arguments.
        MyLogger.Write("Starting process.");

        // 2. Call from another method
        ProcessData();
    }

    static void ProcessData()
    {
        MyLogger.Write("Processing data...");
        
        try
        {
            // Example of logging during an error
            throw new InvalidOperationException("DB connection error");
        }
        catch (Exception ex)
        {
            MyLogger.Write($"Exception occurred: {ex.Message}");
        }
    }
}

// General Logger Class
public static class MyLogger
{
    // Arguments with attributes must have default values.
    public static void Write(string message,
        [CallerFilePath] string file = "",
        [CallerLineNumber] int line = 0,
        [CallerMemberName] string member = "")
    {
        // Example of extracting only the file name as the full path is too long.
        string fileName = Path.GetFileName(file);
        
        string logText = $"[{DateTime.Now:HH:mm:ss}] {fileName} (Line:{line}) {member}() : {message}";
        
        Console.WriteLine(logText);
    }
}

Output Example

[12:00:01] Program.cs (Line:11) Main() : Starting process.
[12:00:01] Program.cs (Line:19) ProcessData() : Processing data...
[12:00:01] Program.cs (Line:28) ProcessData() : Exception occurred: DB connection error

Customization Points

  • Adjusting Log Format: Since CallerFilePath returns an absolute path (e.g., C:\Users\...\Program.cs), you can make logs more readable by using Path.GetFileName or converting it to a relative path from the project root.
  • Argument Order: Since these are treated as optional arguments, they are typically placed at the end of the method’s parameter list.

Points of Caution

  • Requirement for Default Values: Parameters using attributes like [CallerMemberName] must be declared with default values (e.g., = null or = 0). Failure to do so results in a compilation error.
  • Security (Path Leakage): CallerFilePath contains the full path from the compilation environment. Directly displaying this on error screens in web applications risks leaking the server’s directory structure.
  • Resolution at Compile Time: These values are embedded as literals at compile time, not at runtime (like via reflection). Therefore, the original source code information may remain even after using obfuscation tools.

Application

Property Change Notifications in WPF/MAUI

In INotifyPropertyChanged implementations, CallerMemberName is frequently used to avoid the hassle of manually passing the property name as a string.

using System.ComponentModel;
using System.Runtime.CompilerServices;

public class UserViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    // No need to explicitly pass the property name
    protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    private string _name;
    public string Name
    {
        get => _name;
        set
        {
            _name = value;
            // No need to write the string "Name"
            OnPropertyChanged(); 
        }
    }
}

Summary

By utilizing Caller Information Attributes, you are freed from the redundant task of writing the “current location” in your logging code. This is a powerful feature for building logging foundations and implementing property change notifications in the MVVM pattern. Since it embeds information during compilation, the impact on runtime performance is minimal.

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

私が勉強したこと、実践したこと、してることを書いているブログです。
主に資産運用について書いていたのですが、
最近はプログラミングに興味があるので、今はそればっかりです。

目次