Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/319.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何将默认值(TimeSpan)作为可选参数的默认值发出_C#_Dynamic_Reflection_Reflection.emit - Fatal编程技术网

C# 如何将默认值(TimeSpan)作为可选参数的默认值发出

C# 如何将默认值(TimeSpan)作为可选参数的默认值发出,c#,dynamic,reflection,reflection.emit,C#,Dynamic,Reflection,Reflection.emit,我想发射动态方法,与下面完全相同: void Foo(TimeSpan ts = default(TimeSpan)) 通过使用ildasm,我可以看到它被编译为nullref。 然而,从我所能得到的情况来看,如果我想通过发出代码来实现同样的效果,我可以调用名为ParameterBuilder.SetConstant,当可选值类型为TimeSpan时,它将抛出异常。 我甚至反编译了SetConstant方法,它显式地处理DateTime(但不是TimeSpan)。Nullref也是不可接受的。

我想发射动态方法,与下面完全相同:

void Foo(TimeSpan ts = default(TimeSpan))
通过使用
ildasm
,我可以看到它被编译为
nullref
。 然而,从我所能得到的情况来看,如果我想通过发出代码来实现同样的效果,我可以调用名为
ParameterBuilder.SetConstant
,当可选值类型为
TimeSpan
时,它将抛出异常。 我甚至反编译了
SetConstant
方法,它显式地处理
DateTime
(但不是
TimeSpan
)。Nullref也是不可接受的。从该代码中,似乎无法将默认值(
TimeSpan
)设置为默认值。
有人能帮忙吗?

这很难,需要大量使用反射来解决.net framework的限制

正如您所指出的,您可以反汇编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);
}
您还可以反汇编它,并查看从何处引发异常(当类型为值类型时):

以这种方式设置默认值不会更新stacksize,这意味着您必须在获取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#编译到的内容是一样的。

根据您想要实现的内容,确切地说,可能有一种更简单的方法。如果调用
ParameterBuilder.DefineParameter(1,ParameterAttributes.Optional,“Foo”)
,则生成的参数将声明为可选,但没有明确的默认值。在C#中使用此程序集时,您将无法获得默认值的IntelliSense,但编译器仍允许您在不显式提供值的情况下调用该方法,如果您这样做,它将通过
default(TimeSpan)

生成的IL与C#编译器将生成的IL不同(因为缺少参数初始化),我只能猜测其他.NET语言将如何使用这种声明,但它确实节省了
System.Reflection.Emit
(生成的IL通过了验证——运行时本身对默认声明不做任何处理)

请注意,正是因为运行时不使用默认值声明(需要任何工具这样做),所以在动态方法中发出默认值是一种奇怪的做法,应该很少甚至没有实际的应用程序,因为知道调用该方法的任何代码也应该知道要传递的值(在保存到磁盘的程序集中定义它们是有意义的,编译器可以读取)


如果该方法确实是动态的,那么您可能希望生成该方法的多个重载,一个带参数,一个不带参数(一个不带参数的可以调用另一个)。这与具有可选参数的方法具有相同的效果,并且对于动态调用方来说也更易于处理。

添加代码-这是一个显示问题并实际编译和运行的最小示例。请注意,
ParameterBuilder.SetConstant
的文档明确提到了支持的类型,以及
TimeSp根本不包括
(根据规范,它不是标准类型,就像十进制)还有,你为什么要这样做?应该如何使用动态方法?你是否使用它来编译到稍后引用的程序集?默认值是编译时特性,因此除非你从普通C代码调用该方法(无反射等),默认值没有任何意义。你想做什么,为什么你认为默认参数是一个很好的方法?我发现你的问题非常有趣,但你确实应该添加一个你尝试过的代码示例,以及调用SetConstant时得到的异常。好的问题往往会吸引大量好的答案,我认为我认为这是一个非常有趣的问题。如果下面的答案中有一个真的回答了你的问题,请考虑接受其中的任何一个。这将为下一个访问这个橡树园的人节省时间
FieldInfo fi = typeof(ILGenerator).GetField("m_maxStackSize", BindingFlags.NonPublic | BindingFlags.Instance);
            fi.SetValue(ilgen, 8);
.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