Posted on 6/19/2008 2:03:56 AM by Justin Etheredge
(I put a little call stack tutorial at the end, so if you are confused, skip to the end. Then restart. Rinse and repeat.)
A while back I made a post entitled don't be clever. In this post I postulated that in the modern world of programming we need to be more cognizant of making our code readable and grokkable than to make it clever or sneaky. In this post I also used one of my favorite programming quotes of all time (which I will put here again because it is awesome):
Programming is not like being in the CIA, you don't get credit for being sneaky.
Steve McConnell
And that is so true. For 99% of the developers out there, no one is ever going to pat you on the back for being sneaky. You're probably more likely to get punched in the face for it when the team has to spend the weekend debugging a piece of your clever code.
I decided to write this post today because I came across this little gem (which I did not write!!!) that epitomizes exactly why you should not do this. Keep in mind that this is not the exact block of code, but it does mimic part of its functionality. Lets look at the code first:
private static void DoSomething()
{ StackFrame frame = new StackFrame(1);
string methodName = frame.GetMethod().Name;
if (methodName == "Main")
{ //do something
}
else
{ //do something else
}
}
Okay, so what does that do? Well, it looks at the previous stack frame by instantiating a StackFrame object with "1" as a constructor parameter. Then it gets the method of that stackframe and uses it to determine what to do. Pretty clever, huh? A little too clever if you ask me.
So, I have thrown together a little example of why you don't do this. Never ever do this. Only in controlled laboratory environments should this type of code be written, and even then you should be careful that it doesn't escape. So here is my sample app:
static void Main(string[] args)
{ string methodName = ShowMyMethodName();
string message = "Look where I am coming from: {0}"; Console.WriteLine(String.Format(message, methodName));
}
private static string ShowMyMethodName()
{ StackFrame frame = new StackFrame();
return frame.GetMethod().Name;
}
What this does now is it just pulls the current stack frame and displays the name. The reason I changed it to do this will be clear in a moment. So, what happens when we run it?
Huh, wait... it worked. You are probably laughing with satisfaction (if you are slow) or waiting patiently (if you are smart) because you know that I would not have written such an idiotic blog post.
It appears to have worked fine, but now let us compile it in release mode and see how far we get. You do run your production apps in release mode, right?

Now what happened? We are pulling the current stack from, but it is showing that we are calling from Main. But that code isn't in Main, it is in "ShowMyMethodName"! To give you an idea of what just happened, look at this:
static void Main(string[] args)
{ string methodName = ShowMyMethodName();
string message = "Look where I am coming from: {0}"; Console.WriteLine(String.Format(message, methodName));
}
[MethodImpl(MethodImplOptions.NoInlining)]
private static string ShowMyMethodName()
{ StackFrame frame = new StackFrame();
return frame.GetMethod().Name;
}
That worked, and it was all due to that little Attribute we put on our method. In case you are wondering (it is pretty self explanatory), that keeps the method from being inlined (again, tutorial at bottom of post).
Oooooooooooooooh. Now it becomes all too clear. Those darn little compiler tricks. Well, actually not a compiler trick, a JITer trick. The reason we know it is a JIT optimization and not a compiler optimization is because if we use reflector on it (man I love reflector), we still see our happy little methods:
So, even though it compiled as though it would work, it still doesn't work. At runtime the "ShowMyMethodName" method isn't there, it was optimized out! The developer that wrote that code above was lucky that the method that they were coming from wasn't being inlined, but they got lucky!
An even worse situation would have been if I was working on an x86 computer and it worked in debug and release mode. Well the 64-bit JITer might have different heuristics for determining which methods should be inlined. So, it might work on your box in all modes and then not work in production! Lets just say that being clever is bad! Production only bugs are the worst kind of bugs, because far too often our output from the running program is inadequate.
So there you have it. Being tricky will get you in trouble. Potentially big trouble. If you are ever writing a line of code and there is something in the back of your mind that is just nagging at you, saying "you probably shouldn't do that." Listen to it. Please, listen to it. If you ever see this:
long value = GetSomeLongValue();
UseSomeIntegerValue((int)value);
Think if you were a doctor: "Well, this isn't big enough, but if I just squeeze this then I think it will fit in there." Yeah, that would be a career ending choice. We programmers have it so good.
Stack Tutorial
Okay, so for those who aren't in the know, I'm going to give a quick and dirty explanation of how a call stack works. Each time a method is called a new stack frame is pushed onto the stack. It is called the stack because it is a stack data structure, and by that I mean it is LIFO (Last In First Out). You push something onto the top, and then you pop that last item off. The stack is where all of a methods local variables are kept (or pointers to the heap, but that is for another time). So, lets say we call MethodA and then MethodA calls MethodB. Well first MethodA's stack frame is pushed on the stack and then MethodB's is. So, the stack would look something like this:
Okay, so, when I was talking above about methods being inlined, what that means is that the compiler (or in our case the Just In Time compiler, also know as the JITer or JIT compiler) can look at a method and say: "Hmm, that method looks short or rarely used or whatever...I think it would be more efficient to move the operations in that function and just stick them in wherever that method is called. Like this:
So, what happens is that MethodB essentially disappears. This allows the application to avoid the overhead of calling another method and pushing another frame onto the stack. While this overhead is negligible in most cases, it is generally assumed to be a safe optimization and therefore the compiler does it.