CallerMemberName and Interfaces
Anand Mukundan
Posted on September 1, 2018
[CallerMemberName] is a attribute that is available in C# 5.0. It is a very useful functionality especially when doing common app framework components like logging, auditing etc.
One of the things that you usually try to do in your common logging infrastructure is to log the function that generated that log.
So before C# 5.0 it was most common to interrogate the stack to find the caller, which can be an expensive operation. Or the caller information is passed from the source function, which hard codes its name in the call.
Something like
_logger.Debug("Message", "myFunctionName");
This becomes hard to maintain and so in most cases the logger actually interrogates the stack to get this information and as mentioned before that comes with a performance penalty.
Using this new attribute, you can write something like the following for your log function:
public void Debug(string Message, [CallerMemberName] string callerFunction = "NoCaller")
The second parameter here is not passed by the caller, but is rather filled up by the infrastructure (i.e. .NET Framework). The parameter needs to be an optional parameter for you to be able to use this attribute. And this does not have a performance penalty as the compiler will fill in this parameter at compile time and so no run-time overheads.
This works fine, except when you use Interfaces. Again interfaces are generally a pattern that is often used in framework components like logging. So you will have a ILogger interface that will have a definition like about which is implemented by your actual Logger class and the caller use the Interface to talk to your implementation.
If you do something like this, the gotcha is that the interface also needs to have the attribute defined, or the value will not be auto filled. This is something that is easy to miss and is not well documented from whatever I could find. So your interface definition for this function will have the attribute defined like below:
void Debug(string Message, [CallerMemberName] string callerFunction = "")
for the caller value to flow through to your implementation.
When you are designing your interfaces this is so easy to miss and can take a long time to figure out why.
Posted on September 1, 2018
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.