I have been working on an extension to Unity for a while to do interception. It is basically a port that Brad Wilson and I did for ObjectBuilder and the SimpleContainer we built. I first did this before I left patterns & practices and pushed it on to the UnityContrib site. All went well until I wanted to go back and update the code for Unity 1.1. When I ran the unit tests, I got 14 errors and a very odd stack trace:
Parameter name: str
(Strategy type Microsoft.Practices.ObjectBuilder2.DynamicMethodConstructorStrategy, index 0) --->
System.ArgumentNullException: Value cannot be null. Parameter name: str
...
Ok… what the heck is null? Well after digging a little into what that method is doing, it is using the parameter name to emit the dynamic code for the build plans:
// Resolve parameters
ParameterInfo[] parameters = selectedCtor.Constructor.GetParameters();
buildContext.IL.BeginExceptionBlock();
int i = 0;
foreach (string parameterKey in selectedCtor.GetParameterKeys())
{
buildContext.IL.Emit(OpCodes.Ldstr, parameters[i].Name);
buildContext.IL.Emit(OpCodes.Stloc, currentParameterName);
buildContext.EmitResolveDependency(parameters[i].ParameterType, parameterKey);
++i;
}
...
I am confused… I open up ILDASM and look at my dynamic type for interception and see that there is a name for the parameter called A_1. This is where I went wrong in retrospect. What ILDASM was doing was adding a name for my parameter even though I did not have one. How I figured this out was to add some tests to see where my emit code was going wrong. I found that when you add parameters for constructors / methods, they don’t get names and the compiler is perfectly happy to let you do this. So I did a little more reading and found out that I need to change my code from this:
ConstructorBuilder wrappedConstructor = typeBuilder.DefineConstructor(
MethodAttributes.Public | MethodAttributes.HideBySig |
MethodAttributes.SpecialName | MethodAttributes.RTSpecialName,
CallingConventions.HasThis,
parameterTypes.ToArray());
to this:
ConstructorBuilder wrappedConstructor = typeBuilder.DefineConstructor(
MethodAttributes.Public | MethodAttributes.HideBySig |
MethodAttributes.SpecialName | MethodAttributes.RTSpecialName,
CallingConventions.HasThis,
parameterTypes.ToArray());
// Define the name of the parameters for the constructor
wrappedConstructor.DefineParameter(1, ParameterAttributes.None, "ilEmitProxy");
for (int idx = 0; idx < parameters.Length; idx++)
{
wrappedConstructor.DefineParameter(idx + 2, ParameterAttributes.None, parameters[idx].Name);
}
So DefineParameter was the key for me. Although I didn’t really think that was it because I assumed (wrong of course) that it was to define the parameter that I had already defined in the call to DefineConstructor. Well, that shows you what I know.
Guess names really do matter…. although I can never remember them Bob.