# Wednesday, 23 January 2008
« IKVM 0.36 Update 1 Release Candidate 1 | Main | IKVM 0.36 Update 1 Release Candidate 2 »
How To Get an Explicit Interface Implementation Method

Suppose you have a Type that you know implements IEnumerable, how do you get the GetEnumerator method?

Both the developer(s) at Microsoft and the Mono developer(s) that wrote System.Xml.Serialization managed to find the same wrong answer to this question. Here's the code from Mono, but Microsoft does somethig very similar:

MethodInfo met = type.GetMethod ("GetEnumerator", Type.EmptyTypes);
if (met == null) {
  // get private implemenation
  met = type.GetMethod ("System.Collections.IEnumerable.GetEnumerator",
                        BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance,
                        null, Type.EmptyTypes, null);
}

The reason this is wrong is that the name of the private method is an implementation detail of the compiler that was used to compile the code. C# happens to name the private method this way, but other languages may not.

For example, try the following VB webservice:

<%@ WebService Language="VB" Class="Service1" %>

Imports System.Collections
Imports System.Web.Services

Public Class Service1
  Inherits WebService

  <WebMethod()> Public Function HelloWorld() As Frob
    Return New Frob()
  End Function
End Class

Public Class Frob
  Implements IEnumerable

  ' Note that this method is private
  Private Function GetEnumerator() As IEnumerator Implements IEnumerable.GetEnumerator
    Return "frob".GetEnumerator()
  End Function

  Public Sub Add(ByVal obj As Object)
    Throw New NotSupportedException()
  End Sub
End Class

If you run this webservice on .NET, you'll an exception inside System.Xml.Serialization.TypeScope.GetTypeDesc(), because it expects to find a public GetEnumerator method or a private System.Collections.IEnumerable.GetEnumerator method. However, VB allows you to pick the method name (in this case simply GetEnumerator). If you make the GetEnumerator method public, the webservice will work.

The Right Way

Here's the right way to get the method:

MethodInfo getEnumerator = typeof(IEnumerable).GetMethod("GetEnumerator", Type.EmptyTypes);
InterfaceMapping map = type.GetInterfaceMap(typeof(IEnumerable));
int index = Array.IndexOf(map.InterfaceMethods, getEnumerator);
MethodInfo meth = map.TargetMethods[index];

Meta Question

Of course, the real question remains unanswered. Why do we need the MethodInfo in the first place? Wouldn't Xml serialization be better off by simply using the IEnumerable interface?

Wednesday, 23 January 2008 07:37:35 (W. Europe Standard Time, UTC+01:00)  #    Comments [8]
Wednesday, 23 January 2008 11:41:50 (W. Europe Standard Time, UTC+01:00)
According to C# Language Specification http://msdn2.microsoft.com/en-us/vcsharp/aa336809.aspx you needn't implement IEnumeragle at all. To get foreach statement work you only need to provide GetEnumerator() instance method with right signature.
Andrey Titov
Wednesday, 23 January 2008 11:56:28 (W. Europe Standard Time, UTC+01:00)
Andrey,

That's true, but irrelevant. The Xml serialization code is explicitly looking for the GetEnumerator method on types that implement IEnumerable, not GetEnumerator methods that follow the C# foreach pattern.
Wednesday, 23 January 2008 17:19:34 (W. Europe Standard Time, UTC+01:00)
Nice. I never knew that!

Now would there be a a way to do that for Generic interfaces and learn the type parameter types? Does one simply have to use Type.GetInterfaces and manually whether check each element of the result array is of the required (open) Generic interface??
Andy Hume
Wednesday, 23 January 2008 17:51:21 (W. Europe Standard Time, UTC+01:00)
Yes, if you want the generic IEnumerable<> interfaces that are implemented you need to loop through all interfaces and do something like this:

foreach (Type t in type.GetInterfaces())
{
if (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(IEnumerable<>))
{
Console.WriteLine("type implements IEnumerable of {0}", t.GetGenericArguments()[0]));
}
}
Thursday, 24 January 2008 06:32:19 (W. Europe Standard Time, UTC+01:00)
If I remember correctly, this problem also arises in C# because you can do stuff like:

class Thing : IEnumerable
{
public IEnumerator GetEnumerator() { ... }
public IEnumerator IEnumerable.GetEnumerator() { ... }
}

The buggy code in System.Xml would pick the first method, while your fixed version would pick up the second.
Kannan Goundan
Friday, 25 January 2008 00:03:30 (W. Europe Standard Time, UTC+01:00)
Hi,

I have a problem with dynamically linked classloading of an annotation class from c# on a java class with annotations. If I make the same calls in java, and run a test program using ikvm, it succeeds, but as soon the c# call fails. I'm using the ClassPathAssemblyClassLoader in an [assembly: ..] syntax, but I get the exception included below. Any idea how to debug this, or determine what's happening here? It seems obvious that the internal jvm properly supports the annotation I wish to use, but when it's getting linked through dll loads, this fails somehow. Your help would really be appreciated! Oh, and ikvm is great - I'm very impressed with what you've created so far!

java.lang.TypeNotPresentException: Type java.lang.annotation.RetentionPolicy, IKVM.OpenJDK.ClassLibrary, Version=0.36.0.6, PublicKeyToken=13235d27fcbfff58 not present
Caused by: java.lang.ClassNotFoundException: java.lang.annotation.RetentionPolicy, IKVM.OpenJDK.ClassLibrary, Version=0.36.0.6, PublicKeyToken=13235d27fcbfff58
at IKVM.NativeCode.java.lang.Class.forName0(Unknown Source)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:265)
at ikvm.internal.AnnotationAttributeBase.classFromSig(AnnotationAttributeBase.java:314)
at ikvm.internal.AnnotationAttributeBase.decodeElementValue(AnnotationAttributeBase.java:415)
at ikvm.internal.AnnotationAttributeBase.decodeValues(AnnotationAttributeBase.java:247)
at ikvm.internal.AnnotationAttributeBase.setDefinition(AnnotationAttributeBase.java:219)
at java.lang.annotation.$ProxyRetention.<init>(RetentionAttribute.java)
at cli.System.Reflection.CustomAttribute._CreateCaObject(Unknown Source)

at cli.System.Reflection.CustomAttribute.CreateCaObject(Unknown Source)
at cli.System.Reflection.CustomAttribute.GetCustomAttributes(Unknown Source)
at cli.System.Reflection.CustomAttribute.GetCustomAttributes(Unknown Source)
at cli.System.RuntimeType.GetCustomAttributes(Unknown Source)
at IKVM.Internal.CompiledTypeWrapper.GetDeclaredAnnotations(Unknown Source)
at IKVM.NativeCode.java.lang.Class.getRawAnnotations(Unknown Source)
at java.lang.Class.initAnnotationsIfNecessary(Class.java:3091)
at java.lang.Class.getAnnotation(Class.java:3049)
at sun.reflect.annotation.AnnotationType.<init>(AnnotationType.java:131)

at sun.reflect.annotation.AnnotationType.getInstance(AnnotationType.java
:84)
at sun.reflect.annotation.AnnotationParser.parseAnnotation(AnnotationPar
ser.java:220)
at sun.reflect.annotation.AnnotationParser.parseAnnotations2(AnnotationP
arser.java:87)
at sun.reflect.annotation.AnnotationParser.parseAnnotations(AnnotationPa
rser.java:70)
at java.lang.Class.initAnnotationsIfNecessary(Class.java:3091)
at java.lang.Class.getAnnotation(Class.java:3049)
at sun.reflect.annotation.AnnotationType.<init>(AnnotationType.java:131)

at sun.reflect.annotation.AnnotationType.getInstance(AnnotationType.java
:84)
at sun.reflect.annotation.AnnotationParser.parseAnnotation(AnnotationPar
ser.java:220)
at sun.reflect.annotation.AnnotationParser.parseAnnotations2(AnnotationP
arser.java:87)
at sun.reflect.annotation.AnnotationParser.parseAnnotations(AnnotationPa
rser.java:70)
at java.lang.Class.initAnnotationsIfNecessary(Class.java:3091)
at java.lang.Class.getAnnotation(Class.java:3049)
at java.lang.Class.isAnnotationPresent(Class.java:3058)
Johan Proust
Friday, 25 January 2008 07:24:50 (W. Europe Standard Time, UTC+01:00)
Please don't use the weblog comments for support questions or bug reporting. Either file a bug on SourceForge, send a message to the ikvm-developers list or to me directly. Thank you.

BTW, I don't know what's going wrong. I need more details or a repro scenario.
Thursday, 31 January 2008 15:17:35 (W. Europe Standard Time, UTC+01:00)
Many thanks Jeroen. I'll look back at my code that checks interfaces and see if it can be improved by checking for the generic interfaces too (e.g. find a IList<Tx> when now I only can check for IList).
Andy
Andy Hume
Name
E-mail
Home page

I apologize for the lameness of this, but the comment spam was driving me nuts. In order to be able to post a comment, you need to answer a simple question. Hopefully this question is easy enough not to annoy serious commenters, but hard enough to keep the spammers away.

Anti-Spam Question: What method on java.lang.System returns an object's original hashcode (i.e. the one that would be returned by java.lang.Object.hashCode() if it wasn't overridden)? (case is significant)

Answer:  
Comment (HTML not allowed)  

Live Comment Preview