默认参数和反射:如果ParameterInfo.IsOptional那么是DefaultValue总是可靠吗? [英] Default parameters and reflection: if ParameterInfo.IsOptional then is DefaultValue always reliable?

查看:340
本文介绍了默认参数和反射:如果ParameterInfo.IsOptional那么是DefaultValue总是可靠吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在看看 ParameterInfo.IsOptional 是如何定义的(我在内部IOC框架中添加默认参数支持),在我看来, true,不能保证 ParameterInfo.DefaultValue (或者实际上 ParameterInfo.RawDefaultValue 实际上<



如果您查看 IsOptional 给出的MSDN示例,IL似乎可以定义一个可选的参数,但是其中没有提供默认值(因为必须显式提供 ParameterAttributes.HasDefault )。也就是说可能导致参数类型为例如 Int32 ParameterInfo.IsOptional 的情况为真,但 ParameterInfo.DefaultValue 为null。



我的语言是C#,因此我可以处理 >编译器会做。基于这样我可以有一个简单的测试如下(参数这里是一个 ParameterInfo 实例,返回一个要用作参数的运行时参数的实例):

  if(no_config_value)
{
if(!parameter.IsOptional)throw new InvalidOperationException();
//它是可选的,所以读取默认
return parameter.DefaultValue;
}
else
return current_method_for_getting_value();

但我认为有些语言(我想在IL级别,而不是仅仅基于一个特定的编译器做什么)可以在调用者上确定要使用的默认值,如果是,则 default(parameter.ParameterType )



这是一个更有趣的地方,因为 DefaultValue

code>是,显然 DBNull.Value (根据 RawValue 的文档)。如果参数是 object IsOptional == true



做了一些挖掘,我希望可靠的解决方法是物理读取 ParameterInfo.Attributes 成员,阅读bitflags单独第一以检查 ParameterAttributes.Optional ,然后检查 ParameterAttributes.Default 。只有 两者 都存在,那么阅读 ParameterInfo.DefaultValue 将是正确的。



我要开始编写代码和写测试,但我希望有更多的IL知识的人能够确认我的怀疑,并希望确认这是正确的任何基于IL的语言(因此避免需要模拟不同语言的库)。

解决方案

我的问题是没有 - 只是因为 IsOptional 是真的并不意味着 DefaultValue 实际上将包含真实的默认值。我的猜想在问题文本中的更进一步是正确的(和.Net文档有点解释这一点,在一个圆周的方式)。实质上,如果默认存在,那么调用者应该使用它,否则调用者应该提供它自己的默认。参数的 Attributes 用于确定是否存在默认值。



这是我做的。 / p>

假设存在以下方法:

  / *返回默认(T)的FastDefault< T>()* / 
public object FastDefault(Type t){/ * elided * /}


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

  public object GetParameterValue(ParameterInfo p,IDictionary< string,object> args)
{
/ * null检查p和args已省略 - args可以为空虽然* /
对象argValue =空值;
if(args.TryGetValue(p.Name,out argValue))
return argValue;
else if(p.IsOptional)
{
//现在检查是否在IL中使用方法
提供了默认值if((p.Attributes& ParameterAttributes .HasDefault)==
ParameterAttributes.HasDefault)
return p.DefaultValue; //使用提供的默认值
else
return FastDefault(p.ParameterType); //使用FastDefault方法
}
else //参数需要一个参数 - 抛出异常
throw new InvalidOperationException(Parameter requires a argument);
}



我已经测试了这个逻辑对构造函数和方法写成这样: / p>

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

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



然后,的问题,我意识到在C#4中,我们可以使用 OptionalAttribute 生成一个没有默认值的可选参数

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

再次,程序正确地落入执行 FastDefault 方法。



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



我认为它覆盖了所有 - 它在我试过的一切工作很好(我有乐趣,试图获得重载分辨率感觉正确,因为我的IOC系统总是使用命名参数的等价物 - 但C#4规范帮助那里)。


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.

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.

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

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.

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!

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.

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!).

解决方案

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.

This is what I've done.

Assume the following method 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; }
}

I.E, where a default is supplied in addition to the parameter being optional (the program correctly falls into the branch which reaches for ParameterInfo.DefaultValue).

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

Again, the program correctly falls into the branch which executes the FastDefault method.

(In this case C# too will use the type's default as the argument for this parameter)

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).

这篇关于默认参数和反射:如果ParameterInfo.IsOptional那么是DefaultValue总是可靠吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆