About Jolt.NET Libraries

Inspired by the Boost C++ libraries, Jolt.NET aims to complement the .NET Base Class Library (BCL) with algorithms, data structures, and general productivity tools. It is the hope of the authors that the features of Jolt.NET will one day be part of, or represented in the BCL and the .NET Framework.

Frustration with Reflection.Emit API

Over the past few months, I've been working on adding generics support to the Jolt.Testing library. I've been travelling too, so I haven't had as much time as I would have liked to spend on development.

I'm currently focusing on generic type definitions (generic method definitions will come later). For a given generic real subject type, the ProxyTypeBuilder needs to generate a generic interface and generic proxy type that match in number of arguments. Furthermore, the proxy's arguments must specialize the interface, and the interface must carry the same constraints as the real subject type. These requirements are demonstrated by the following example.



public class UserType<A,B,C>

where A : class

where B : struct

where C : A

{

bool Method() { return true; }

}



public interface IUserType<A,B,C>

where A : class

where B : struct

where C : A

{

bool Method();

}



public sealed class UserTypeProxy<Q,R,S> : IUserType<Q,R,S>

{

bool IUserType<Q,R,S>.Method() { return m_realSubjectType.Method(); }

private readonly UserType m_realSubjectType = new UserType<Q,R,S>();

}




In writing the code to implement this type of code generation, I believe I found a bug in the Reflection.Emit API. For reference, here is the code that I believe should emit the class interface and proxy relationship.


void CreateTypes(ModuleBuilder module)

{

TypeBuilder contractInterface = module.DefineType("IUserType", TypeAttributes.Public TypeAttributes.Interface TypeAttributes.Abstract TypeAttributes.AutoClass TypeAttributes.AnsiClass);

contractInterface.DefineGenericParameters("A", "B", "C");

MethodBuilder interfaceMethod = contractInterface.DefineMethod("Method",

MethodAttributes.Abstract MethodAttributes.HideBySig MethodAttributes.NewSlot MethodAttributes.Virtual MethodAttributes.Public,

typeof(bool), Type.EmptyTypes);



TypeBuilder implementationType = module.DefineType("UserTypeProxy", TypeAttributes.Public TypeAttributes.Sealed TypeAttributes.AutoClass TypeAttributes.AnsiClass TypeAttributes.BeforeFieldInit);

GenericTypeParameterBuilder[] implementationParams = implementationType.DefineGenericParameters("Q", "R", "S");

implementationType.DefineDefaultConstructor(MethodAttributes.Public MethodAttributes.SpecialName MethodAttributes.RTSpecialName);

MethodBuilder implementationMethod = implementationType.DefineMethod(contractInterface.FullName + "<Q,R,S>.Method",

MethodAttributes.Private MethodAttributes.HideBySig MethodAttributes.NewSlot MethodAttributes.Virtual MethodAttributes.Final,

typeof(bool), Type.EmptyTypes);



ILGenerator ilgen = implementationMethod.GetILGenerator();

ilgen.Emit(OpCodes.Ldc_I4_0);

ilgen.Emit(OpCodes.Ret);



Type specializedContract = contractInterface.CreateType().MakeGenericType(

implementationParams[0], implementationParams[1], implementationParams[2]);

implementationType.AddInterfaceImplementation(specializedContract);

implementationType.DefineMethodOverride(implementationMethod, interfaceMethod);

contractInterface.CreateType();

implementationType.CreateType();

}




Unfortunately, executing this code yields the following exception.

System.TypeLoadException : Type 'UserTypeProxy' from assembly 'test, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' tried to override method 'IUserType.Method' but does not implement or inherit that method.

I’ve done a considerable amount of experimenting with this code, and I’m no longer sure that I have implemented the routine correctly. The error message is confusing, because the code sure looks like it is creating a method override for a method that exists. What as more confusing is the the documentation for DefineMethodOverride, which states that the method call is not needed when overriding a base-class or interface method. However, I’ve found this to be untrue when explicitly implementing an interface. Also, the above routine works as expected when I removed the generic parameters from the routine, including the MakeGenericType call; you will notice that this case is almost identical to the code path that is currently checked in for non-generic types.

My research carried me to this bug report, which is very similar in nature to my issue, but it is unclear whether the bug is fixed in .NET 3.5. A look at mscorlib.dll shows a version of 2.0.0.0, which hasn't changed since the release of .NET 2.0 so the answer is likely "not fixed".


If there is a bug in the Reflection.Emit API, then this is a dissapointing setback as the bug is unlikely to be resolved in the near-term. In that time period, I would have to restrict the code generation to implement interfaces implicitly and doing so would limit the allowable methods from a real subject type to those that do not override another via an explicit interface implementation.


public class RealSubjectType : IDisposable

{

// One of these methods will be unavailable in IRealSubjectTypeProxy.

void IDisposable.Dispose() { /* do something */ }

public void Dispose() { /* do something else */ }

}




In the mean time, I'll keep tinkering with the code and determine if it is worthwhile to continue with the approach of explicit interface implementation. Who knows, maybe a reader will point out the fault with my current code generation implementation? :)

1 comments:

Steve Guidi said...

For dealing with explicit generic interface implementations, I spoke with some of my colleagues at work and was referred to the static TypeBuilder.GetMethod() method.

I haven't done much investigation into the purpose and functionality of this method, but the general idea to resolving the issue in this post is to specialize the interface method before overriding it.

In the second code sample from the post, line 39 shows the specialization of the interface. I thought that after this specialization, I can access any method from the interface and expect it to inherit its parent type arguments when referenced in an explicit interface implementation (lines 9, 45). This is not the case, and in light of this idea, the code I wrote doesn't make much sense anymore.

I should be able to replace line 45 with the following code:

MethodInfo specializedMethod = TypeBuilder.GetMethod(specializedContract, interfaceMethod);
implementationType.DefineMethodOverride(implementationMethod, specializedMethod);

I have not yet verified this, but plan to do so when revisiting the explicit interface implementation features.