A while ago I wrote about using reflection for testing non-public members. Since then I've been increasingly annoyed by the repetitive nature of that approach. At some point it occurred to me that it should be possible to automate the mundane and boring task of writing the same kind of reflection code over and over again. However, there are two options for code generation each with some severe disadvantages. You can generate source code which means coupling the solution to a particular programming language and build system or you generate code at run-time which is completely useless if there is no late binding support and using reflection is exactly what you're trying to avoid. Fortunately, one of the new features of C# 4.0 is late binding so please allow me to introduce the wrapper factory! First we'll need a sample type we'd like to test:

public class TestClass

{

    private int _fooInvocations;

 

    private TestClass()

    {

    }

 

    private int Foo()

    {

        return _fooInvocations++;

    }

}

The first problem is that there are no public constructors. So let's make a wrapper and get us an instance of TestClass:

dynamic constructorWrapper = WrapperFactory.CreateConstructorWrapper(typeof(TestClass));

TestClass testClass = constructorWrapper.CreateInstance();

All the method CreateConstructorWrapper() needs is the type whose constructors we want to call. What we get back is an object of a type created a runtime that has a CreateInstance() method for each constructor of the type we passed to CreateConstructorWrapper() (including public constructors) with the same signature as the original constructor. Now if the constructor were the only concern we'd be done but there are also these non-public members. No problem, on to the next factory method:

dynamic wrapper = WrapperFactory.CreateInstanceWrapper(testClass);

Console.WriteLine("Return value of testClass.Foo():    {0}", wrapper.Foo());

Console.WriteLine("Value of testClass._fooInvocations: {0}", wrapper.__fooInvocations);

As you can see the instance wrapper exposes the members under the same names (this again includes public members) and in case of methods with the exact same signature. The names of fields, however, are always prefixed with an underscore in order to solve the issue with types that contain members with the exact same name which unfortunately is very common (the C# compiler does that for events). What I haven't shown so far (namely events, properties and generic methods) is supported in the same way and just omitted to keep this post short and to the point. Finally, there is also the method WrapperFactory.CreateStaticWrapper() that takes a type and generates a wrapper similar to the instance wrapper shown above but instead exposing all static members of a type.

When I say these wrappers expose all members/constructors it is a bit of a simplification. There are certain types that cannot be supported or would lead to unverifiable code. For these reasons the following are not supported in any way:

  • Pointers
  • The types System.ArgIterator, System.RuntimeArgumentHandle and System.TypedReference
  • The use of generics with non-public types as generic type constraints
  • Methods with a varargs parameter

In addition, while the factory does generate members that involve non-public types you will not be able to use them. The C# runtime binder treats these cases like non-public members and will throw and exception (I'm not sure yet if/how I want to deal with this). 

Before you (hopefully) go ahead and give it a try just a quick reminder that the wrapper factory is experimental software (I'd call it an Alpha) built with and on top of pre-release software so please keep that in mind and under no circumstances blindly trust it at this point. I'd love to get general feedback as well as bug reports in order to bring the wrapper factory to perfection and would like to ask you to use the EMTF project page on CodePlex for that purpose and not the contact form on this blog.

This posting is provided "AS IS" with no warranties, and confers no rights.