The problem is ParameterBuilder.SetConstant
, not Activator.CreateInstance
. SetConstant
is a function for determining the default value for an optional parameter and requires that the specific value be provided as a constant. For ref classes, null
is a valid specific value, but for value classes, before creating Nullable<>
, null
not a valid value. How do you, for example, unpack null
in int
? So, SetConstant
checked the value types to see if the particular value was passed as a null
constant and throws an ArgumentException
as a more descriptive error than the NullReferenceException
you would get when unpacking null into a value class.
In the case of Nullable<>
, null
now a valid concrete value for the class of values. Nullable<>
is itself a class of values, as shown in Activator.CreateInstance
, and unboxing null
to a Nullable<int>
value makes sense. Here SetConstant
has an error: it does not take this into account and throws a โdescriptiveโ error, which is not really an error. Instead of fixing it from Microsoft, any SetConstant
call with a null Nullable<>
will have to implement the behavior protected by the wrong conditional. This means that you can delve into the private methods and fields of ParameterBuilder
using reflection. Here is the code I made to handle this particular case. The standard SetConstant
function should be used in situations that do not reflect an error.
//ModuleBuilder module : a parameter passed into the containing function, the module which is being built (which contains the method that has the optional parameter we are setting the constant for) //ParameterBuilder optParam : A parameter passed into the containing function, the parameter that is being built which is to have the null default value. MethodInfo method = typeof(TypeBuilder).GetMethods(BindingFlags.Static | BindingFlags.NonPublic) .Where(m => m.Name == "SetConstantValue" && m.GetParameters().Length > 0 && m.GetParameters()[0].ParameterType.Name == "RuntimeModule") .Single(); var RuntimeHandle = typeof(ModuleBuilder).GetMethod("GetNativeHandle", BindingFlags.NonPublic | BindingFlags.Instance); method.Invoke(null, new object[] { RuntimeHandle.Invoke(module, new object[]{}), optParam.GetToken().Token, 0x12, null });
I reported a bug to Microsoft. They replied that it would not be fixed in .NET 3.5, but it was added to the internal error database.
UPDATE:
Bug fixed in .NET 4.0. ParameterBuilder.SetConstant
now has a branch to the constant == null
condition, which checks whether the value type is generalized, obtained from Nullable<>
, and throws an exception only if it is not.
Kevin fee
source share