Coding curiosity 5: Ambiguous member overloading

No VB versus C# flamewar would be complete without the obligatory reference to the fact that both .NET languages support 99% of the same functionality. While this is obviously true, there are some subtle implementation differences that can catch you out if you regularly switch between the two languages.

Class member overloading allows you to keep a consistent interface by calling a single logical method regardless of the type of data passed to that method. But it's relatively easy to create situations where it's difficult for a code review to establish which version of an overloaded member will actually be called.  The following C# console application sets the task of determining which method is invoked by each call to testOverloads.WriteLine().

The C# compiler performs overload resolution at each level of the inheritance hierarchy, starting with the most derived class. This breadth-first scan means that it will pick the most derived method that is suitable. In this case, the screenshot below shows the methods called by the C# code above.

So what happens when you run the exact VB equivalent of this C# code?

The VB compiler, unlike the C# compiler, looks across the whole inheritance tree before making a decision about which of the overloaded methods is most suitable. Because Overloads in VB actually means hide by name and signature, the VB compiler takes the view that you are essentially adding a new member to the base class in this case. So this VB code produces the following result:

If you're surprised by this, wait for two more posts about method overload resolution, where you'll see that the C# compiler has a couple of quirks that are definitely non-intuitive.

[UPDATE: Fixed C# code typo.]

[UPDATE: Fixed text typo.]