默认参数和反射:如果ParameterInfo.IsOptional那么DefaultValue总是可靠的吗?

And*_*tan 6 .net compiler-construction reflection

我正在研究如何ParameterInfo.IsOptional定义(我正在向内部IOC框架添加默认参数支持),在我看来,如果为true,则无法保证ParameterInfo.DefaultValue(或实际上ParameterInfo.RawDefaultValue)实际上是默认值适用.

如果你看一下给出IsOptionalMSDN示例,在IL中似乎可以定义一个可选的参数但是没有提供默认值(假设ParameterAttributes.HasDefault必须显式提供).即可能导致一种情况,一个参数的类型是,比如说Int32,ParameterInfo.IsOptional是真实的,但ParameterInfo.DefaultValue为空.

我的语言是C#,因此我可以什么工作,编译器就行了.基于此我可以进行如下简单测试(parameter这是一个ParameterInfo实例,该方法旨在返回一个实例,用作参数的运行时参数):

if(no_config_value)
{
  if(!parameter.IsOptional) throw new InvalidOperationException();
  //it's optional, so read the Default
  return parameter.DefaultValue;
}
else
  return current_method_for_getting_value();
Run Code Online (Sandbox Code Playgroud)

但我认为某些语言(我希望在IL级别上做到这一点,而不仅仅是基于一个特定的编译器所做的)可以将责任放在调用者上以确定要使用的默认值,如果所以,default(parameter.ParameterType)需要按顺序进行.

这是一个有趣的地方,因为DefaultValue显然DBNull.Value(根据文档RawValue),如果没有默认值.如果参数是类型object和,这是不好的IsOptional==true!

已经做了较为挖掘一下,我希望,可靠的方式来解决,这是物理读取ParameterInfo.Attributes成员,读bitflags单独第一,检查ParameterAttributes.Optional检查ParameterAttributes.Default.只有两者都存在,那么阅读才是ParameterInfo.DefaultValue正确的.

我将开始编写代码并编写测试,但我希望有更多IL知识的人可以证实我的怀疑,并希望确认这对任何基于IL的语言都是正确的(因此避免模​​拟不同语言的库的负载!).

And*_*tan 6

对我的问题的简短回答是否定的 - 只是因为这IsOptional是真的并不意味着DefaultValue实际上会包含真正的默认值.我在问题文本中更进一步的假设是正确的(.Net文档确实有点解释这个问题,以一种全面的方式).实质上,如果存在默认值,则调用者应该使用它,否则调用者应该提供它自己的默认值.参数Attributes用于确定是否存在默认值.

这就是我所做的.

假设存在以下方法:

/* wrapper around a generic FastDefault<T>() that returns default(T) */
public object FastDefault(Type t) { /*elided*/ }
Run Code Online (Sandbox Code Playgroud)

然后给出一个特定的参数和提供的参数值的Dictionary(来自配置):

public object GetParameterValue(ParameterInfo p, IDictionary<string, object> args)
{
  /* null checks on p and args elided - args can be empty though */
  object argValue = null;
  if(args.TryGetValue(p.Name, out argValue))
    return argValue;
  else if(p.IsOptional)
  {
    //now check to see if a default is supplied in the IL with the method
    if((p.Attributes & ParameterAttributes.HasDefault) == 
        ParameterAttributes.HasDefault)
      return p.DefaultValue;  //use the supplied default
    else
      return FastDefault(p.ParameterType); //use the FastDefault method
  }
  else  //parameter requires an argument - throw an exception
    throw new InvalidOperationException("Parameter requires an argument");
}
Run Code Online (Sandbox Code Playgroud)

然后我在构造函数和方法上测试了这个逻辑:

public class Test
{
  public readonly string Message;
  public Test(string message = "hello") { Message = message; }
}
Run Code Online (Sandbox Code Playgroud)

IE,除了参数是可选的之外还提供默认值(程序正确地落入到达的分支中ParameterInfo.DefaultValue).

然后,在回答我问题的另一部分时,我意识到在C#4中我们可以使用它OptionalAttribute来生成一个没有默认值的可选参数:

public class Test2
{
  public readonly string Message;
  public Test2([OptionalAttribute]string message) { Message = message; }
}
Run Code Online (Sandbox Code Playgroud)

同样,程序正确地落入执行该FastDefault方法的分支中.

(在这种情况下,C#也将使用类型的默认值作为此参数的参数)

我认为这涵盖了所有 - 它在我尝试过的所有事情上都很好用(我很乐意尝试让重载决策感觉正确,因为我的IOC系统总是使用相当于命名的参数 - 但C#4规范帮助了那里).