通过反射获取可选Guid的DefaultValue?

And*_*lai 3 c# reflection value-type optional-parameters base-class-library

我有以下代码,我用它作为示例来说明不同的场景:

    public static void MethodWithOptionalGuid(Guid id = default(Guid)) { }

    public static void MethodWithOptionalInteger(int id = 2) { }

    public static void MethodWithOptionalString(string id = "33344aaa") { }

    public static void MethodWithoutOptionalParameter(int id, Guid longId) { }

    static void Main(string[] args)
    {
        var methods = typeof(Program).GetMethods(BindingFlags.Public | BindingFlags.Static).ToList();

        foreach (var method in methods)
        {
            PrintMethodDetails(method);
        }

        Console.ReadLine();
    }

    static void PrintMethodDetails(MethodInfo method)
    {
        Console.WriteLine(method.Name);

        foreach (var parameter in method.GetParameters().ToList())
        {
            Console.WriteLine(parameter.Name +
                              " of type " + parameter.ParameterType.ToString() +
                              " with default value:" + parameter.DefaultValue);
        }

        Console.WriteLine();
    }
Run Code Online (Sandbox Code Playgroud)

它打印以下内容:

MethodWithOptionalGuid
id of type System.Guid with default value:

MethodWithOptionalInteger
id of type System.Int32 with default value:2

MethodWithOptionalString
id of type System.String with default value:33344aaa

MethodWithoutOptionalParameter
id of type System.Int32 with default value:
longId of type System.Guid with default value:
Run Code Online (Sandbox Code Playgroud)

对于最后3种方法,输出似乎很好.

我的问题是关于第一个,MethodWithOptionalGuid:为什么Guid的默认值不被识别?

我希望得到类似"0000000 -..."的东西.我还尝试使用新的Guid()和相同的结果初始化可选参数.我也试过其他结构,比如TimeSpan,行为是一样的.

我期望所有值类型的行为都相同(如整数示例所示).

额外:我尝试在Asp.Net MVC中使用可选的Guid参数并且失败(必须使Guid可以为空)时发现了这个东西.通过MVC代码,发现它在某些时候使用DefaultValue.所以我制作了这个代码示例以更好地说明我的问题.

Han*_*ant 5

当你想要做这样的事情时,为什么非常相关,你需要注意的事情.龙住在这里.如果您使用ildasm.exe查看该方法,那么您将看到:

  .param [1] = nullref
Run Code Online (Sandbox Code Playgroud)

null是结构类型的默认值,呃哦..param指令的语义在CLI规范Ecma-335第II.15.4.1.4节中描述.它很短,我会复制粘贴整个章节(为便于阅读而编辑)

MethodBodyItem :: = .param'['Int32']'['='FieldInit]

该伪指令在元数据中存储与方法参数号Int32相关的常量值,参见§II.22.9.虽然CLI要求为参数提供值,但某些工具可以使用此属性的存在来指示工具而不是用户是否要提供参数的值.与CIL指令不同,.param使用索引0指定方法的返回值,索引1指定方法的第一个参数,索引2指定方法的第二个参数,依此类推.

[注意:CLI不会对这些值附加任何语义 - 完全取决于编译器实现他们希望的任何语义(例如,所谓的默认参数值).结束说明]

[注意]是最相关的.当您使用Reflection时,您将扮演编译器的角色,由您来正确解释默认值.换句话说,您必须知道在C#中,结构类型的默认值为null.

否则这是可以存储在[FieldInit]中的限制.初始化值必须存储在程序集的元数据中,并受[attribute]声明的相同类型的限制.可以使用简单的值类型,而不是结构.因此按照惯例,C#设计者通过使用null来解决这个问题.足以让C#编译器理解这default(Guid)是有意的.

这不是麻烦结束的地方,还有其他方法来指示可选参数,[OptionalAttribute]在C#v4.0之前使用.今天仍然使用COM互操作库和其他语言,如VB.NET和C++/CLI.这就是龙的生活地点.

暂时忽略那些野兽,你所拥有的解决方法代码可能如下所示:

    var def = parameter.DefaultValue;
    if (parameter.ParameterType.IsValueType && def == null) {
        def = Activator.CreateInstance(parameter.ParameterType);
    }
    Console.WriteLine(parameter.Name +
                      " of type " + parameter.ParameterType.ToString() +
                      " with default value:" + def);
Run Code Online (Sandbox Code Playgroud)