默认参数与反思:如果ParameterInfo.IsOptional则是默认值总是可靠吗?则是、默认值、可靠、参数

由网友(画地为牢)分享简介:我在寻找如何 ParameterInfo.IsOptional 定义(我加入到内部IOC框架默认参数的支持),而且在我看来,当如此,也不能保证该 ParameterInfo.DefaultValue (或者实际上 ParameterInfo.RawDefaultValue )是的实际上的应用的是默认值。I'm loo...

我在寻找如何 ParameterInfo.IsOptional 定义(我加入到内部IOC框架默认参数的支持),而且在我看来,当如此,也不能保证该 ParameterInfo.DefaultValue (或者实际上 ParameterInfo.RawDefaultValue )是的实际上的应用的是默认值。

I'm looking at how ParameterInfo.IsOptional is defined (I'm adding default parameter support to an internal IOC framework), and it seems to me that, when true, there is no guarantee that ParameterInfo.DefaultValue (or indeed ParameterInfo.RawDefaultValue) are actually the default values that are to be applied.

如果你看一下 MSDN的例子给出了 IsOptional ,似乎可以在IL定义参数是可选的,但对于没有默认提供(考虑到 ParameterAttributes.HasDefault 必须明确提供)。即可能导致这种情况的参数类型,比方说,的Int32 ParameterInfo.IsOptional 是真实的,但 ParameterInfo.DefaultValue 为空。

If you look at the MSDN example given for IsOptional, it seems possible in IL to define a parameter that is optional but for which no default is supplied (given that the ParameterAttributes.HasDefault must be explicitly supplied). I.e. potentially leading to a situation that a parameter type is, say, Int32, ParameterInfo.IsOptional is true, but ParameterInfo.DefaultValue is null.

我的语言是C#,所以我可以工作什么的是的编译器会做。在此基础上,我可以有一个简单的测试,如下所示(参数这里是信息参数实例,该方法是指要用作运行时的参数为所述参数返回一个实例):

My language is C#, therefore I can work on what that compiler will do. Based on that I can have a simple test as follows (parameter here is a ParameterInfo instance, and the method is meant to return an instance to be used as the runtime argument for the parameter):

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();

但我想,有些语言(我希望得到这个权利在IL-水平,而不是仅仅依据是什么一个特定的编译器),可以将责任上的来电的确定缺省值使用,如果是这样,一个默认(parameter.ParameterType)将需要解决的。

But I'm thinking that some languages (and I want to get this right at the IL-level, rather than just based on what one particular compiler does) can place the onus on the caller to determine the default value to be used, if so, a default(parameter.ParameterType) would need to be in order.

这是它得到更有趣,因为默认值是,显然的DBNull.Value (根据为 RawValue )如果没有默认值。这是没有好,如果该参数的类型是对象 IsOptional ==真

This is where it gets a little more interesting, because DefaultValue is, apparently DBNull.Value (according to the documentation for RawValue) if there is no default. Which is no good if the parameter is of type object and IsOptional==true!

做完多一点挖,我希望,可靠的方式来解决,这是物理读 ParameterInfo.Attributes 成员,分别读取bitflags 第一以检查 ParameterAttributes.Optional 和然后的检查 ParameterAttributes.Default 。只有当的两个的是present,然后读 ParameterInfo.DefaultValue 将是正确的。

Having done a bit more digging, I'm hopeful that the reliable way to solve this is to physically read the ParameterInfo.Attributes member, reading the bitflags individually first to check for ParameterAttributes.Optional and then check for ParameterAttributes.Default. Only if both are present, then reading ParameterInfo.DefaultValue will be correct.

我要开始编码和写作解决这个测试,但我要问,希望有一个人有更多的IL的知识,可以证实我的怀疑,希望确认这将是正确的任何基础IL语言(从而避免需要小样库负荷在不同的语言!)。

I'm going to start coding and writing tests around this, but I'm asking in the hope that there's someone with more IL knowledge that can confirm my suspicions and hopefully confirm that this'll be correct for any IL-based language (thus avoiding the need to mock up loads of libraries in different languages!).

推荐答案

简短的回答我的问题是没有 - 只是因为 IsOptional 是真实的,并不意味着默认值实际上将包含真正的默认。我推测在问题文本进一步下跌是正确的(和.NET文档也有点解释这一点,在一个迂回的方式)。在本质上,如果存在缺省值,那么调用者应该使用它,否则呼叫者应提供它自己的默认。该参数的属性被用来计算出,如果存在缺省值。

The short answer to my question is no - just because IsOptional is true doesn't mean that DefaultValue will actually contain the real default. My suppositions further down in the question text were correct (and the .Net documentation does kinda explain this, in a round-about way). In essence, if a default exists, then the caller should use it, otherwise the caller should provide it's own default. The parameter's Attributes are used to figure out if a default exists.

这是我做了什么。

假设下面的方法存在:

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

和再给予一个特定的参数和提供的参数值的字典(从配置):

And then given a particular parameter and Dictionary of supplied argument values (from configuration):

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");
}

然后,我测试的构造函数和方法这样写这样的逻辑:

I've then tested this logic on constructors and methods written like this:

public class Test
{
  public readonly string Message;
  public Test(string message = "hello") { Message = message; }
}

IE浏览器,其中默认除了参数是可选的提供(该程序正确地落入其手伸向分支 ParameterInfo.DefaultValue )。

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

Then, in answer to another part of my question, I realised that in C# 4 we can use the OptionalAttribute to produce an optional parameter with no default:

public class Test2
{
  public readonly string Message;
  public Test2([OptionalAttribute]string message) { Message = message; }
}

此外,程序正确落入其执行 FastDefault 法的分支。

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

我觉得,涵盖了一切 - 它的一切工作很好,我试过(我有乐趣试图让重载感觉正确的,因为我的IOC系统始终使用命名参数相当于 - 但C#4规格的帮助有)。

I think that covers it all - it's working nicely on everything I've tried (I have had fun trying to get overload resolution feeling correct as my IOC system always uses the equivalent of named arguments - but the C# 4 spec helped there).