Enum参数的DefaultValue和RawDefaultValue的意外差异

时间:2016-11-30 11:54:59

标签: c# .net reflection enums system.reflection

考虑以下示例:

class Program
{
    static void Main(string[] args)
    {
        foreach(var par in typeof(A).GetMethod("Method").GetParameters())
        {
            Console.WriteLine("Def {0}, RawDef {1}",
                par.DefaultValue, par.RawDefaultValue);
        }
    }
}

class A
{
    public void Method(int a = 5, B b = B.b){}
}
enum B
{
    a = 0, b = 1
}

根据RawDefaultValueDefaultValue的文档,在StackOverflow的支持下,这两种访问默认值的方法应返回相同的数据。

但相反,我得到以下输出:

Def 5, RawDef 5
Def b, RawDef 1

因此,显然,RawDefaultValue会删除有关参数的信息,即枚举类型。

我的问题:这是一个错误,还是文档的另一部分证明了这一点?

有趣的事实:在Mono上它返回

Def 5, RawDef 5
Def b, RawDef b

2 个答案:

答案 0 :(得分:3)

tl; dr:这不是一个错误,它是一个功能......

正如您在文档中看到的那样,RawDefaultValue支持仅反射上下文,而DefaultValue则不支持。

现在,如果我们检查两种方法的源代码,我们会看到它使用System.Reflection.MdConstant标记调用GetValue的方法bool raw方法。

由于System.Reflection希望根据其所处的上下文向您提供“最佳”信息,因此它宁愿为您提供enum而不是原始值(原始值可以是从enum字段得出结论,相反的情况不起作用。)

现在我们可以在System.Reflection.MdConstant.GetValue中看到:

if (fieldType.IsEnum && raw == false)
{
    ...
    switch (corElementType) //The actual enum value
    {
        ...
        case CorElementType.I4:
            defaultValue = *(int*)&buffer;
            break;
        ...
    }
    return RuntimeType.CreateEnum(fieldType, defaultValue);
}

在您的情况下,返回B.b // = 1

但是调用RawDefaultValue会使该标志false成为:{/ p>

switch (corElementType)
{
    ...
    case CorElementType.I4:
        return *(int*)&buffer;
    ...
}

在您的情况下,返回1

如果您尝试使用System.RuntimeType.CreateEnum调用Reflection(因为它是内置的),并且仅反射上下文已加载Assembly,您将获得InvalidOperationException 1}}:

//Change to 'Assembly.Load' so the last line will not throw.
Assembly assembly = Assembly.ReflectionOnlyLoad("DllOfB");

Type runtimeType = Type.GetType("System.RuntimeType");
MethodInfo createEnum = runtimeType.GetMethod("CreateEnum", /*BindingFlags*/);

MethodInfo getRuntimeType = typeof(RuntimeTypeHandle).GetMethod("GetRuntimeType", /*BindingFlags*/);
Type bType = assembly.GetType("DllOfB.B");

//Throws 'InvalidOperationException':
object res = createEnum.Invoke(null, new [] { getRuntimeType.Invoke(bType.TypeHandle, new object[]{}), 1 });

至于支持从Mono返回B.b的{​​{1}}, 这意味着RawDefaultValue不支持Mono仅反射上下文 或者它可以以某种方式从DefaultValue创建Type的实例,这可能在不同的架构中 - 这是不太可能的。

答案 1 :(得分:1)

从查看.NET参考资料来看,这一切似乎都是有意无意的:

public override Object DefaultValue { get { return GetDefaultValue(false); } }
public override Object RawDefaultValue { get { return GetDefaultValue(true); } }

private Object GetDefaultValue(bool raw)
{
    // ... Precheck
    object defaultValue = GetDefaultValueInternal(raw);
    // ... Postprocessing
    return defaultValue;
}

[System.Security.SecuritySafeCritical]
private Object GetDefaultValueInternal(bool raw)
{
    // ... Too long to include, but it contains "if (raw)"
    // ... a couple of times to do different things.
    return defaultValue;
}

来源:
https://referencesource.microsoft.com/#mscorlib/system/reflection/parameterinfo.cs,565

如果你认为.NET是开源的,如果你认为它应该表现不好,只需做一个修复并提交一个Pull Request; - )