System.TypeLoadException: Method does not have an implementation.

I’m working on a C# project where I have a WCF service using an external library and providing some functionality through a REST interface. For some reason, it broke at some point in time and I was getting the following error message:

System.TypeLoadException: Method ‘XXX’ in type ‘YYY’ from assembly ‘ZZZ’ does not have an implementation.

First, since I had the code for the external library (I actually built it from source), I checked the code. The method XXX was actually available with an implementation in type YYY.

Here is how the different assemblies look like:

Assemblies

After a few hours I did figure out that the problem was that I had referenced an old version of the interfaces assembly when building the implementation assembly and a new version of the interfaces assembly when building the services assembly. So the implementation was actually there. It was just not marked as being the implementation of the method defined in the interface but was just marked as an additional method present in the implementation. So accessing the method through the interface in the service failed.

After recompiling the implementation assembly with a reference to the newest interfaces assembly solved the problem.

Since I have over 15 years of experience on the Java platform and about 4 years of experience on the .NET platform, I tend to always relate everything I see on the .NET platform to the way it works in Java. So after finding the solution to this issue, I decided to check how the behavior in Java would be in this case.

I’ve first created an interface:

public interface IMyInterface {
	public void myFirstMethod();
}

Then I’ve created an implementation:

public class MyImplementation implements IMyInterface {
	public void myFirstMethod() {
		System.out.println("myFirstMethod");
	}

	public void mySecondMethod() {
		System.out.println("mySecondMethod");
	}
}

Finally, I’ve implemented a service instantiating the implementation and calling the first method through the interface:

public class MyService {
	public static void main(String[] args) {
		IMyInterface myInt = new MyImplementation();
		myInt.myFirstMethod();
	}
}

Running the service displays:

myFirstMethod

Then I’ve updated the interface adding the second method:

public interface IMyInterface {
	public void myFirstMethod();
	public void mySecondMethod();
}

I’ve then added a call to the second method in the service without recompiling the implementation with the new interface:

public class MyService {
	public static void main(String[] args) {
		IMyInterface myInt = new MyImplementation();
		myInt.myFirstMethod();
		myInt.mySecondMethod();
	}
}

Compiling the service again and running it displays:

myFirstMethod
mySecondMethod

So I didn’t get the same issue here.

Note that this post is not about which platform is better and whether the Java behavior makes more sense than the .NET behavior or not.
With the way Java behaves I wouldn’t have run into this problem. On the other hand, I wouldn’t have noticed that I built the implementation classes against the wrong version of the interface. If the implementation actually didn’t implement mySecondMethod, I wouldn’t have noticed it either and would have got an exception like:

Exception in thread "main" java.lang.AbstractMethodError: MyImplementation.mySecondMethod()V
at MyService.main(MyService.java:5)

Also if I had handled versioning properly on the .NET side, I would probably not wasted that much time. I guess it’s always the thing with versioning: it’s easier to assume that there is only one version of a given interface and that all components use the same one, than handling multiple versions and having different assemblies built against different versions of another assembly. On the other hand, assuming this often just means that if you do not have a build system making sure that all components are recompiled against the latest version of an interface, you’ll get some nice exceptions at runtime.

Leave a Reply

Your email address will not be published. Required fields are marked *