可空属性与Nullable局部变量

时间:2013-05-05 05:00:24

标签: c# reflection nullable

我对Nullable类型的以下行为感到困惑:

class TestClass {
    public int? value = 0;
}

TestClass test = new TestClass();

现在,Nullable.GetUnderlyingType(test.value)返回基础Nullable类型,即int。 但是,如果我尝试获取像这样的字段类型

FieldInfo field = typeof(TestClass).GetFields(BindingFlags.Instance | BindingFlags.Public)[0];

我调用

Nullable.GetUnderlyingType(field.FieldType).ToString()

它返回System.Nullable[System.Int32]类型。这意味着方法Nullable.GetUnderlyingType()具有不同的行为,具体取决于您获取成员类型的方式。为什么会这样?如果我只是使用test.value,如何在不使用反射的情况下告诉它Nullable

5 个答案:

答案 0 :(得分:11)

smartcaveman的答案是迄今为止最好的答案,它实际上标识了描述此行为的文档部分。

这种行为是不可取的,也是不幸的;这是由于三种特征相结合的行为,它们本身表现得相当合理。

这三个特征是:

  • GetType是一种非虚方法;它不能被覆盖。这应该是有道理的;对象无法确定报告的类型。通过使其成为非虚拟,该方法可以保证说实话。

  • 传递给this上声明的非虚方法的object值必须转换为object;因此,对于价值类型的对象,调用GetType()的接收者将被加框为object

  • 可以为空的值类型没有盒装形式;当你打开一个可以为空的int时,你得到一个盒装的int或者你得到一个空引用。您永远不会获得对盒装可空int的有效引用。

每个特性本身都是合理的,但结合起来是不可取的:当你在有效的可空int上调用GetType时,运行时将nullable int置于一个盒装的int中,然后将其传递给{{当前报告this的{​​{1}}的1}}。如果该值为null nullable int,则运行时框为null,然后在空引用上调用object.GetType并崩溃。这些行为都不可取,但我们坚持使用它们。

答案 1 :(得分:8)

可空类型有点奇怪。但是,至少他们的行为有很好的记录。

来自MSDN上的C#编程指南, “如何:识别可空类型”athttp://msdn.microsoft.com/en-us/library/ms366789(VS.80).aspx

您还可以使用System.Reflection命名空间的类和方法来生成表示Nullable类型的Type对象。但是,如果尝试使用GetType方法或is运算符在运行时从Nullable变量获取类型信息,则结果是一个Type对象,它表示基础类型,而不是Nullable类型本身。 

在Nullable类型上调用GetType会导致在将类型隐式转换为Object时执行装箱操作。因此,GetType始终返回表示基础类型的Type对象,而不是Nullable类型。

值得指出的是,标题中的区别是不准确的。类型行为不是基于局部变量或属性,而是基于是通过运行时对象还是通过反射(或使用typeof运算符)来访问类型。你的推理是可以理解的,因为局部变量的类型通常只能通过运行时对象访问,但是它有缺陷,因为如果你在运行时通过属性访问器访问一个可空对象,那么它的行为将等同于一个局部变量。

另外,要明确回答问题的最后一部分:告诉test.value在没有使用反射的情况下可以为空的唯一方法是访问它并获取NullReferenceException (当然, ,只有当test.value为null时才会发生。正如所写,在你的例子中,该值不为null,因此无法进行反射决定

答案 2 :(得分:3)

问题在于您假设Type的{​​{1}}与test.value Type的{​​{1}}相同,而且它是不。获取field.FieldType value实际上获得了字段中存储的Type,在这种情况下为0.

test.value

相同(在您的示例中)

Type

要演示,请将Type t = test.value.GetType()初始化为null,Type t = 0.GetType()会抛出value

答案 3 :(得分:0)

方法GetNulllableUnderlyingType为可空类型返回基础类型,对于其他类型返回null;

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine(GetNulllableUnderlyingType(new TestClass().value));
        Console.WriteLine(GetNulllableUnderlyingType(new TestClass()));
        Console.ReadKey();
    }

    public class TestClass
    {
        public int? value;
    }

    public static Type GetNulllableUnderlyingType<T>(T value)
    {
        Type result =  Nullable.GetUnderlyingType(typeof (T));
        return result;
    }
}

答案 4 :(得分:0)

首先,您的示例中的Nullable.GetUnderlyingType(test.value)将无法编译。或者我在评论中看到typeof(test.value)

让我们通过以下修改后的代码回答这个问题:

TestClass test = new TestClass();
Type type1 = test.value.GetType(); // Gets the type of the data inside the field ==> System.Int32.
Type underlyingType1 = Nullable.GetUnderlyingType(type1); // Gets the underlying type of System.Int32 ==> null.

FieldInfo field = typeof(TestClass).GetFields(BindingFlags.Instance | BindingFlags.Public)[0];
Type type2 = field.FieldType; // Now the type is retrieved of the field, not the data inside. ==> System.Int32?
Type underlyingType2 = Nullable.GetUnderlyingType(type2); // Gets the underlying type of System.Int32? ==> System.Int32.

在字段上执行GetType()时,您将获得内部数据的类型,而不是字段本身的类型。例如:

object o = 1;
Type type = o.GetType();

在此示例中,当您致电GetType()时,您还会获得System.Int32,而不是System.Object。因为在您致电GetUnderylingType之前,在您开始时使用的是不同的类型,最终会得到不同的结果。