A number of years ago .NET added a small feature called Caller Member Info. This isn’t the biggest feature out there, and you’re not going to use this frequently unless you’re doing a lot of XAML development, but it’s good to have in your tool belt.
Caller Member Info attributes allow you to simplify your code by providing more information to individual methods without having to pass in explicit values.
In this short article I’ll give you an overview of the caller member info attributes, how they work, and the problems they can solve.
The Problem
Let’s say you have code that does some standard checks:
There’s some obvious code duplication here. We can clean it up by extracting a method:
Okay, cool. We now have logic in one method, but having to pass in the method name is a little irritating.
This is where CallerMemberNameAttribute
comes into play. Let’s take a look at an improved version of the code and then discuss how it works:
Note here that the code is barely changed. We added [CallerMemberName]
to the methodName
parameter on CheckInput
and also made methodName
optional by giving it a default value of an empty string.
The optional parameter allows the code to compile, and then .NET inspects the CallerMemberName
attribute at runtime and injects IL code to switch from the default value to that of the name of the method that just called the executing method.
For example, in DoBar()
, CheckInput
is called and the methodName
parameter is not specified. Instead of taking the default value of ""
, the CallerMemberName
attribute sets the value of methodName
to DoBar
, the caller.
How is this useful?
Admittedly it’s an interesting parlor trick for developer parties (I don’t know for sure, I don’t get invites to those), but how is this actually useful?
For one, logging methods can frequently benefit from knowing who called them.
For another, if you do work with model view view model frameworks (MVVM) such as XAML that rely on things like INotifyPropertyChanged
, you frequently need to state the name of the property that changed. This allows our code to be very simple:
We also copy and paste code more than we’d like to admit – even small pieces of code. Why? Because we hate boilerplate and logging, validation, and property changed notifications are all boiler-plate things.
If we copy and paste and forget to change the name, mistype it, or we rename a method or property down the road without changing the string value, these all can lead to bugs and confusion. Admittedly, the nameof
keyword can help with the renaming scenario, but the point remains: Code you don’t have to write won’t break.
This is a point I’m quite fond of making, but it’s true. If you don’t have to code something, you won’t make a mistake doing it.
Other Caller Member Info Attributes
Okay, so we talked at length at how CallerMemberName
gets us the name of the method or property that invoked the method.
Beyond that, we also have the CallerLineNumberAttribute and CallerFilePathAttribute. Both work similarly to CallerMemberNameAttribute
(though CallerLineNumber
works with an integer) and will provide orienting information on the source code as it existed at compile time.
Realistically you won’t use these nearly as much as CallerMemberName
. The only reason I’ve needed either of these two attributes has been around error logging code.
If you’d like to read more, check out Microsoft’s documentation on Caller Member Info.
If you do use any of these attributes for something I haven’t mentioned, I’d love to hear about it. Please send me a note in the comments or on twitter.
The post Caller Member Info, C#, and You appeared first on Kill All Defects.
Top comments (5)
I used callerfilepathattribute to create a slick unit Test database. I store data for a unit test method in a file next to the source file. I have a test method that writes its argument to the database if shift and alt are down, otherwise it checks the string against the database. Makes it easy to veeify complicated strings have not changed.
This sounds similar to how Snapper works for identifying snapshot files for a unit test.
I've used this in the past for some internal infrastructure pieces... but the one thing I don't like about this attribute is that it requires you to add a parameter to a method that the caller should not use.
It's like I create an API and expose something that I don't want the caller to use.
This breaks several SOLID principles and is downright confusing for those not familiar with how the
[CallerMemberName]
attribute works.So, I normally don't expose any infrastructure that uses it and I try to keep its use very limited.
That said, it can be useful, and I'm sure there are devs out there that will discover it through this post.
Thanks!
Loved the article!!
Didn't know about this feature until now. When should I use it instead of overload or just passing an nullable parameter?
My argument is that you should always use CallerMemberName in cases where you want to pass a member name as it prevents mistakes.
Really, the only scenario I could use it and don't is like this:
In this example, if I didn't pass in caller to the other method via the overload, caller would default to
LogSomething
.So, really, that's the only case where I would explicitly set
caller
- when I need to pass data around and don't wantCallerMemberName
to overwrite the value.