Came across an interesting bug the other day in the MS newsgroups, it appears to be a bug in the 2.0.50727.42 release of the runtime. If this issue is affecting you, you can either use the updated 2.0.50727.97 revision of the runtime which seems to have this issue resolved or you can avoid the situation as I will show below.
The following C# code will illustrate the issue.
using System;
namespace InterfaceMapTest { class MyApp { static void Main(string[] args) { MyControl obj = new MyControl(); obj.Paint(); ((IControl)obj).Paint(); IControl iControl = (IControl) obj; iControl.Paint(); } }
public interface IControl { void Paint(); }
public class Control : IControl { public virtual void Paint() { Console.WriteLine("Control.Paint"); }
void IControl.Paint() { Console.WriteLine("Control.IControl.Paint"); } }
public class MyControl : Control, IControl { } } |
Listing 1: Code shows bug in 2.0.50727.42 version of runtime |
C:\>C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\csc.exe foo.cs
Microsoft (R) Visual C# 2005 Compiler version 8.00.50727.42
for Microsoft (R) Windows (R) 2005 Framework version 2.0.50727
Copyright (C) Microsoft Corporation 2001-2005. All rights reserved.
C:\>foo
Control.Paint
Control.Paint
Control.Paint
This code should print Control.IControl.Paint the second two calls. This worked properly in version 1.x of the framework (and in later revisions of 2.0). At first I thought the C# compiler might be getting a bit confused but after a quick look with reflector I came received the following IL.
.entrypoint .maxstack 1 .locals init ( [0] ConsoleApplication21.InterfaceMapTest.MyControl control1, [1] ConsoleApplication21.InterfaceMapTest.IControl control2) L_0000: nop L_0001: newobj instance void ConsoleApplication21.InterfaceMapTest.MyControl::.ctor() L_0006: stloc.0 L_0007: ldloc.0 L_0008: callvirt instance void ConsoleApplication21.InterfaceMapTest.Control::Paint() L_000d: nop L_000e: ldloc.0 L_000f: callvirt instance void ConsoleApplication21.InterfaceMapTest.IControl::Paint() L_0014: nop L_0015: ldloc.0 L_0016: stloc.1 L_0017: ldloc.1 L_0018: callvirt instance void ConsoleApplication21.InterfaceMapTest.IControl::Paint() L_001d: nop L_001e: ret |
Listing 2: IL of main method |
Which is correct, the ILASM’ed version of the same IL when run in 2.0.50727.97 will work properly (thanks to Bart De Smet for verifying this). Basically what it happening is that the runtime is getting confused by the re-implementation of the interface on the derived class. It only seems to happen when the same signature method on the base is virtual as well. If you run into this problem you may be able to not re-implement the interface on the derived class and the issue will go away. As this item has already been fixed I guess it just needs to be released so there is no feedback you can click through to vote etc.