Today on FAIC, a detective story.
Suppose you have this class in assembly Alpha.DLL:
namespace Alpha { public class Bravo { public void Charlie() { System.Console.WriteLine("Alpha Bravo Charlie"); } } }
Pretty straightforward. You call this method from assembly Delta.EXE like this:
namespace Delta { public interface IFoxtrot { void Charlie(); } public class Echo : Alpha.Bravo, IFoxtrot { } public class Program { static void Main() { IFoxtrot foxtrot = new Echo(); foxtrot.Charlie(); } } }
Notice that class Echo
does not re-implement Charlie
, but that’s fine; the base class implementation suffices to meet the requirements of interface IFoxtrot
. If we run this code and put a breakpoint on Charlie
the call stack window in Visual Studio says:
Alpha.dll!Alpha.Bravo.Charlie() [External Code] Delta.exe!Delta.Program.Main() [External Code]
It’s unsurprising that there is “external code” inserted that calls Main
for you, but what’s with the “external code” between Main
and Charlie
? That should just be a straight call, right? Something strange is going on here.
It is possible for debugging purposes to programmatically inspect the call stack — that is, to write a program that prints out its own call stack, rather than simply examining it in a debugger. (You should not do this for purposes other than debugging because the jitter is allowed to rearrange the call stack as it sees fit. Inlined methods do not appear on the call stack, tail recursive methods do not appear on the call stack, and so on. Use the caller information attributes in C# 5 if you want to write a method that knows what its caller was.) So let’s change the implementation of Charlie
to print out the caller. (Alternatively, as commenter leppie points out, Visual Studio has a “show external code on the call stack” feature that we could use as well, but for our purposes here let’s just print it out.)
public void Charlie() { System.Console.WriteLine("Alpha Bravo Charlie"); System.Console.WriteLine((new System.Diagnostics.StackFrame(1).GetMethod().Name)); }
And now if we run it we get the output
Alpha Bravo Charlie Delta.IFoxtrot.Charlie
What the heck is going on here?
What’s going on is: the CLR requires that any method which implements an interface method be a virtual method, but the only candidate, Alpha.Bravo.Charlie
, is non-virtual. Therefore when generating class Echo
, the C# compiler actually generates the code as though you’d written:
public class Echo : Alpha.Bravo, IFoxtrot { void Delta.IFoxtrot.Charlie() { base.Charlie(); } }
The explicit interface implementation is a virtual method, satisfying the CLR. The compiler-generated method is marked as not having any source code, so the debugger writes External code
in the call stack.
Mystery solved!
Extra credit mystery: Why did I have to specify that Echo
and Bravo
be in different assemblies? Leave your guesses in the comments.
Special thanks to Stack Overflow users Timwi and Jeff Moser for the inspiration for this blog post.
Next time on FAIC: Just why are these adventures so fabulous anyway?
It’s not *much* of a challenge if you skip the extra credit question, go and read the SO question and answer, and then attempt it.
True that.
So it shows [External Code] and if you turn on the option to ‘Show External Code’ will it show Delta.IFoxtrot.Charlie() ?
Hah, I never even thought of that. Yes, of course it does. Silly me.
no wonder, with your skills you must have been typing these programs in notepad 😛 totally forgetting VS 🙂
In the case someone wants to learn more about Show External Code like I did: https://blogs.msdn.com/b/zainnab/archive/2010/10/24/show-external-code-vstipdebug0031.aspx
And now I know about Caller Info Attributes and a problem I once had has now been solved.
I guess that in the same assembly (better,same compilation unit?) the compiler would have detected the non-virtual method and complained?
…or maybe just adjusted Alpha.Charlie to be “virtual”, and compile in a “normal” way, without the stub? I would bet on the first though..
Your second guess is the right one; the C# compiler silently makes Alpha.Bravo.Charlie both virtual and sealed.
Because the C# compiler is smart enough to mark Bravo.Charlie() as virtual for you when it detects that Echo (a subclass of Bravo in the same assembly) needs it to be virtual in order to implement IFoxtrot.
The C# compiler would have no opportunity to make this optimization if Bravo is in a different assembly than Echo, because Bravo’s assembly would have to be compiled before the code in Echo can be analyzed.
And I do assume it’s a performance optimization the compiler is making when the two classes are in the same assembly; that way, calls to Echo.Charlie() entail one virtual dispatch rather than an indirection plus a virtual dispatch.
On the other hand, calls to Bravo.Charlie() would then always pay the penalty of a virtual dispatch, counter-balancing the performance gain for Echo.Charlie(). So I know that haven’t quite figured it all out yet.
You are 90% of the way there. Alpha.Bravo.Charlie is marked as both virtual *and* sealed, which means that the jitter can eliminate the virtual indirection should it choose to. If the jitter knows that a virtual method will only ever have exactly one implementation, it can dispatch directly to it. Whether it does this optimization or not, I don’t know.
“If the jitter knows that a virtual method will only ever have exactly one implementation, it can dispatch directly to it. Whether it does this optimization or not, I don’t know.”
Oh, come on !!
“The jitter” is not a single entity. Sure Eric could find out if a particular version of a particular IL to “machine code” compiler, in a particular scenario, does or does not make the optimization ( we’re talking about x86, x64, Itanium, Silverlight, Windows Mobile, Windows Phone, Windows RT and let us not forget those which are not even maintained by Microsoft like Mono for Mac or Mono for iOS, the latter being somehow forced to have everything prejitted ahead of time – still an IL to machine code compilation, etc, etc X all their versions X many situations and cases ).
But in a situation like this it is better to simply say that “it depends on many many things which are outside of my control” or the shorter “I don’t know”.
I recommend you read this older “FAIC” post:
http://blogs.msdn.com/b/ericlippert/archive/2011/11/18/why-il.aspx
This immediately reminded me of Chris Brumme’s decade-old blog post on the same topic, which also answers the extra credit question:
http://blogs.msdn.com/b/cbrumme/archive/2003/05/03/51381.aspx
“the CLR requires that any method which implements an interface method be a virtual method”
Yes, but why? I suppose it could be related to an implementation detail of the dynamic dispatch on interfaces…
@Michael, thanks for the link, Chris Brumme says something relevant to my question:
“There’s a bit of a debate on our team whether this is an architectural requirement or an implementation detail. At this point, we’re comfortable that we could remove this restriction without much work. However, we have to consider the ECMA standard, the impact on other CLI implementations like Compact Frameworks, the effect on the various languages targeting the compiler, and some interesting effects on existing applications. We might be saddled with this rule indefinitely.”
Ok, then I guess my question does not have a simple answer 🙂 Let’s just say that’s how they chose to do it.
Pingback: A Mexican soccer icon entered politics. Prosecutors say narcos… - صيدله سين وجيم