C# Attributes – Caller Information

As I started venturing into Unity3D and C# I found myself missing couple of useful preprocessor macros from C. The three macros I was missing the most were __FILE____LINE__ and __FUNCTION__. One could argue that those three are the closest thing to what one could call Reflections in C.

I didn’t know what the equivalent for those macros were or if there even was one in C#. Albeit, I was mainly doing thing inside Unity3D so this wasn’t even an issue. So I did some research on Reflections for the blog and eventually also on Attributes and I stumbled upon this thing called Caller Information.

Although Caller Information isn’t really the same thing as the macros mentioned earlier, I think it’s close enough. With Caller Information you can obtain information about the caller to a method.

Here’s a little example of how those preprocessor macros can be helpful.

printf("[%s:%s()::%d] Warning: Input string is empty\n", __FILE__, __FUNCTION__, __LINE__);
// Output:
//  [utils.c:rstrip()::256] Warning: Input string is empty

Right away you can see where the warning was generated, and if you need to react or even disable the message, you will locate it instantly.

As far as I know, C# doesn’t provide anything similar to this. So, we’re going to make something that produces similar outcome.

using System;
using System.Runtime.CompilerServices;

namespace Attributes_Caller_Information_01 {
    class Program {
        static void Debug(string value, 
            [CallerMemberName] string name = "",
            [CallerFilePath] string filePath = "",
            [CallerLineNumber] int lineNumber = 0) 
        {
            Console.WriteLine("[{0}:{1}()::{2}]\n\t" + value, filePath, name, lineNumber);
        }
        static void Main(string[] args) {
            Debug("Just a simple example");
        }
    }
}

// Output:
// [<pathname>\Program.cs:Main()::16]
//        Just a simple example

There might be a better way for handling this, but for this short post, I think this example is sufficient enough. The important bits here are the CallerMemberNameCallerFilePath and CallerLineNumber, which are part of the System.Runtime.CompilerServices. It’s required that what ever variable is defined after the attribute is defined with a default value. That’s all there is to it.

Unfortunately those attributes cannot be used when calling the method, so you can’t use them like this:

Console.WriteLine("{0}", [System.Runtime.CompilerServices.CallerFilePath]);

For more information about the the Caller Information, check the documentation.

Conclusions

The solution is quite ugly and I might return with a better implementation at some point. I would have liked to extend System.Console.WriteLine() and add Caller Information fields to that extension, but it seems that it’s either impossible or unnecessarily difficult.

Caller Information attributes can still be useful when debugging with a shotgun. Meaning that if you’re really not sure where the issue is within your code, you could insert dozens of Debug() messages and see how far the execution goes. Still, it’s a stupid idea. Using breakpoints and a real debugger would be much better option.

 

 

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.