Skip to main content

Antonio Cisternino's Home Page

Go Search
Home
  
Antonio Cisternino's Home Page > My Blog > Invoking private methods through interfaces?!?!? You can!  

My Blog: Invoking private methods through interfaces?!?!? You can!

Title

Invoking private methods through interfaces?!?!? You can! 

Body

This evening we spent near one hour to work out an amazing problem! We were implementing few lines of code to read data from a DB through OleDb provider and OleDbDataReader. We noticed that this class implements the well known System.Collections.IEnumerable interface which consists of a single method, GetEnumerator. We wrote the following code:
 
OleDbDataReader myReader = ...;
IEnumeration e = myReader.GetEnumerator();
 
The compiler complained about the non existing method GetEnumerator in class OleDbDataReader! I was puzzled: the class implements an interface and you can't call its implementation!
I went into the Framework documentation I wasn't able to find the GetEnumerator method into the member list, though the class is reported implementing IEnumerable interface.
I moved to ildasm of System.Data.dll looking for the method in the OleDbDataReader class: at first sight I wasn't able to find the method in the list. I was completely brain damaged: how is it possible?
Now that I've worked it out I feel rather stupid, though I think this is worth a post. However I will go through the entire process I followed to find the answer.
First of all I've disassembled System.Data.dll with ildasm to easily search for the GetEnumerator method. I found in fact the method with a find in the text. I found the method in the end, and with the following signature:
 
.method private hidebysig newslot virtual final
        instance class [mscorlib]System.Collections.IEnumerator
        System.Collections.IEnumerable.GetEnumerator() cil managed
 
Thus I realized that the method name was System.Collections.IEnumerable.GetEnumerator rather than GetEnumerator, so I eventually found the method in the GUI version of ildasm.
At that point I was really puzzled! How can I define a private method in a class to implement an interface?
After a quick try with the following class:
 
using System.Collections;
public class Foo : IEnumerable {
  private IEnumerator GetEnumerator() {
    return null;
  }
}
 
which produced a compilation error. At that point I thought that in C# wasn't possible to define a private method for an interface, so I thought that it was a trick done on the IL.
So I compiled the above class with the public keyword instead of private. Then I disassembled the generated DLL and changed the method signature from
 
.method public hidebysig newslot virtual final
        instance class [mscorlib]System.Collections.IEnumerator
        GetEnumerator() cil managed
to
 
.method private hidebysig newslot virtual final
        instance class [mscorlib]System.Collections.IEnumerator
        System.Collections.IEnumerable.GetEnumerator() cil managed
 
I successfully compiled the assembly with ilasm, though when I tried to load the type a verification exception was risen. Then I paid more attention to the GetEnumeration method of OleDbDataReader and I noticed that the first line in the IL version of the method was:
 
  .override [mscorlib]System.Collections.IEnumerable::GetEnumerator
 
I went back to the IL file, and I've added the same line, and... It worked! The method was private and the class Foo was implementing the interface IEnumerable.
At that point I wondered what would have happened if an instance of Foo would be casted to IEnumerable, and afterwards the GetEnumerator method was called. I wrote the following class, half expecting that it would have worked:
 
using System;
using System.Collections;
class Baz {
  public static void Main(string[] args) {
    Foo f = new Foo();
    IEnumerable a = f;
    IEnumerator e = a.GetEnumerator();
    Console.WriteLine(e == null);
  }
}
 
As expected it worked so I was able to call a private method through an interface. This was reasonable: the vtable of the interface contains a pointer to the code and it would be reasonable for the runtime to allow method invocation through the interface.
Now that I worked out the mechanism I wondered if it make sense at all, and why you can obtain an enumerator to a OleDbDataReader only if it is considered a reference to the IEnumerable interface. Moreover I was wondering by what mechanism they have changed permissions when... I realized that I've forgot something!
In C# you can specify different implementations for a method depending on the actual type of the reference used to invoke it. So I thought that probably the private method is just the way to express into IL the following class:
 
using System.Collections;
public class Foo : IEnumerable {
  IEnumerator IEnumerable.GetEnumerator() {
    return null;
  }
}
 
In this case we are defining the implementation of GetEnumerator only if it is accessed through a IEnumerable reference. If you try to access the method as follows you'll get an error:
 
Foo f = ...;
IEnumeration e = f.GetEnumerator();
 
In the end the answer was far more simple than I first thought, nevertheless it is interesting that in the metadata there is no clear evidence of the C# pattern.
In conclusion beware: if you find a private method of an interface it simply comes from the implementation of the interface version only. Moreover there is little or no tace at all in the documentation that is derived from binaries rather than sources. Finally remember that you can find more than a single implementation of a method in a class!
It has been the first time I found such a design pattern in a real framework, although I think that in CLR 2.0 the OleDbDataReader wouldn't implement the IEnumerable interface at all! I'm not sure that the ability of having different implementations of a method is a feature to be used often.
I know, if you read the post backward everything seems to be rather trivial, nonetheless for me it wasn't (and I was going forward:-), so I hope you've found the post not too boring!

Expires

 

Category

Programming 
Attachments
Created at 4/27/2004 21:20  by Antonio Cisternino 
Last modified at 4/27/2004 22:37  by Antonio Cisternino