这非常困难,需要大量使用反射来解决 .net 框架的限制。
正如您所指出的,您可以反汇编 ParameterBuilder.setConstant。此方法调用内部方法:
[SecuritySafeCritical]
public virtual void SetConstant(object defaultValue)
{
TypeBuilder.SetConstantValue(this.m_methodBuilder.GetModuleBuilder(), this.m_pdToken.Token, (this.m_iPosition == 0) ? this.m_methodBuilder.ReturnType : this.m_methodBuilder.m_parameterTypes[this.m_iPosition - 1], defaultValue);
}
你也可以反汇编,看看异常是从哪里抛出的(当 type 是值类型时):
if (destType.IsValueType && (!destType.IsGenericType || !(destType.GetGenericTypeDefinition() == typeof(Nullable<>))))
{
throw new ArgumentException(Environment.GetResourceString("Argument_ConstantNull"));
}
TypeBuilder.SetConstantValue(module.GetNativeHandle(), tk, 18, null);
幸运的是,您可以在此处调用相同的方法,但从 mscorlib 动态调用:
AssemblyName aName = new AssemblyName("DynamicAssemblyExample");
AssemblyBuilder ab =
AppDomain.CurrentDomain.DefineDynamicAssembly(aName, AssemblyBuilderAccess.RunAndSave);
ModuleBuilder mb =
ab.DefineDynamicModule(aName.Name, aName.Name + ".dll");
TypeBuilder tb = mb.DefineType("MyClass", TypeAttributes.Public);
MethodBuilder meb = tb.DefineMethod("Foo", MethodAttributes.Public | MethodAttributes.Static | MethodAttributes.HideBySig, typeof(void), new Type[] { typeof(TimeSpan) });
ParameterBuilder pb = meb.DefineParameter(1, ParameterAttributes.Optional | ParameterAttributes.HasDefault, "ts");
MethodInfo getNativeHandle = typeof(ModuleBuilder).GetMethod("GetNativeHandle", BindingFlags.NonPublic | BindingFlags.Instance);
object nativeHandle = getNativeHandle.Invoke(mb, new object[0]);
int tk = pb.GetToken().Token;
MethodInfo setConstantValue = typeof(TypeBuilder).GetMethods(BindingFlags.NonPublic | BindingFlags.Static).Where(mi => mi.Name == "SetConstantValue" && mi.GetParameters().Last().ParameterType.IsPointer).First();
setConstantValue.Invoke(pb, new object[] { nativeHandle, tk, /* CorElementType.Class: */ 18, null });
ILGenerator ilgen = meb.GetILGenerator();
FieldInfo fi = typeof(ILGenerator).GetField("m_maxStackSize", BindingFlags.NonPublic | BindingFlags.Instance);
fi.SetValue(ilgen, 8);
ilgen.Emit(OpCodes.Ret);
tb.CreateType();
ab.Save("DynamicAssemblyExample.dll");
以这种方式设置默认值不会更新堆栈大小,这意味着您必须在获得 ILGenerator 之后立即手动设置它(再次通过反射):
FieldInfo fi = typeof(ILGenerator).GetField("m_maxStackSize", BindingFlags.NonPublic | BindingFlags.Instance);
fi.SetValue(ilgen, 8);
这会生成以下 IL:
.method public hidebysig static
void Foo (
[opt] valuetype [mscorlib]System.TimeSpan ts
) cil managed
{
.param [1] = nullref
// Method begins at RVA 0x2050
// Code size 1 (0x1)
.maxstack 8
IL_0000: ret
} // end of method MyClass::Foo
这与您提供的 C# 编译成的内容相同。