Freigeben über


Caller Information – a new concept in Visual Studio 2012

With the recent release of Visual Studio 2012, there are many new features and updates to explore. One of the new features is the concept of Caller Information, available in both C# and VB. Caller Information allows you to obtain certain information about the caller to a method. More specifically, it allows you to get the name of the calling method as well as the source file information and line number.

 

Many customers currently use catch blocks to document the name of the current method and maybe even the source file from which an exception is thrown. But Caller Information provides a way to get this information (and more – source line info), even if no exception occurs. The way it works is that you include attributes as optional parameters in your function’s signature. There are three of these, and they’re defined in the System.Runtime.CompilerOptions namespace. You should supply a default value to whichever ones you include in your function’s signature.

CallerFilePath – The full path and filename (during compile time) of the caller.

CallerLineNumber – The line number (in the source code) where the caller calls the callee.

CallerMembername – The method or property name of the caller.

 

Caller Information can be of particular value for profiling or tracing tools. However, you don’t need to write a profiler in order to take advantage of this new feature. One example where an application could make use of Caller Information would be if you have a function (say, MyFunc) which is called from many different functions. Let’s say MyFunc takes an argument or two, and occasionally MyFunc gets an exception when using the arguments – for example, a NullReferenceException. But the fact that MyFunc doesn’t reproduce this exception consistently may make it difficult to determine what the code path is that results in the exception.

If MyFunc captured the calling function, you’d have a much better idea of how to reproduce the problem and, therefore, find root cause and a resolution.

 

Here’s a demonstration of how to use CallerInformation. I created a console application with the following code. (Sincere apologies as to the format shown below. I struggled for a while to remove the individual boxes around each block of code, but doing so only removed all the indentation, which made the code more difficult to read).

 
 using System.Runtime.CompilerServices;

namespace CallerInfoSample
{
 class Program
{ 
 static void Main(string[] args)
{
 string str = "";
str = Func1();

Console.WriteLine(str);
Console.ReadLine();
 }
 
static string Func1([CallerFilePath] string sFilePath = "", [CallerLineNumber] int iLineNbr = 0, [CallerMemberName] string sMemberName = "")
{
 string s = "";
s = "Before calling Func2, Func1 reports the line number where " + sMemberName + " called the current function is " + sFilePath + ", Line " + iLineNbr + ".";
Console.WriteLine(s);
Console.WriteLine("=====");

bool b = Func2();

s = "After calling Func2, Func1 reports the line number where " + sMemberName + " called the current function is still " + iLineNbr + " since we haven't finished executing Func1.";
Console.WriteLine("Func2 returns " + b.ToString() + ".");
Console.WriteLine(s);
Console.WriteLine("=====");

return "Done Processing";
 }
 
static bool Func2([CallerFilePath] string sFilePath = "", [CallerLineNumber] int iLineNbr = 0, [CallerMemberName] string sMemberName = "")
{
 string s = "";
s = "Before calling Func3, Func2 reports the line number where " + sMemberName + " called the current function is " + sFilePath + ", Line " + iLineNbr + ".";
Console.WriteLine(s);
Console.WriteLine("=====");

int j = 0;
int i = Func3(j);

return true;
 }
 
static int Func3(int i, [CallerFilePath] string sFilePath = "", [CallerLineNumber] int iLineNbr = 0, [CallerMemberName] string sMemberName = "")
{
 string s = "";
s = "Before returning to Func2, Func3 reports the line number where " + sMemberName + " called the current function is " + sFilePath + ", Line " + iLineNbr + ".";
Console.WriteLine(s);
Console.WriteLine("=====");

return (i + 1);
 }
 }
 }
 
 

It results in the following output:

Before calling Func2, Func1 reports the line number where Main called the current function is c:\Samples\CallerInfoSample\CallerInfoSample\Program.cs, Line 16.=====Before calling Func3, Func2 reports the line number where Func1 called the current function is c:\Samples\CallerInfoSample\CallerInfoSample\Program.cs, Line 29.=====Before returning to Func2, Func3 reports the line number where Func2 called the current function is c:\Samples\CallerInfoSample\CallerInfoSample\Program.cs, Line 47.=====Func2 returns True.After calling Func2, Func1 reports the line number where Main called the current function is still 16 since we haven't finished executing Func1.=====Done Processing 

For more information on Caller Information, see MSDN at https://msdn.microsoft.com/en-us/library/hh534540.aspx.

NOTE: I could only make use of Caller information when I configured VS2012 to compile against 4.5 of the .NET Framework; the functionality isn't there in 4.0.

Comments

  • Anonymous
    September 19, 2012
    I wrote similar functionality a while back (more info here: bloggingabout.net/.../12215.aspx) but this proved to be rather expensive so we chose to only enable it when necessary. I'd love to now if there's a performance penalty for using this? Kind regards

  • Anonymous
    September 23, 2012
    Rick, I've not seen any documentation on the perf impact of this, so I'd need to test it myself.  I might delve into this if I can find the time. Nevertheless, the old mantra always applies - test, test, test before going to production.

  • Anonymous
    November 22, 2016
    nice article...