可空属性与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如何在不使用reflection的情况下判断它是否为Nullable

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

从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类型。

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

另外,要明确回答问题的最后一部分: 告诉test.value在没有使用reflection的情况下可以为空的唯一方法是访问它并获得NullReferenceException (当然,只有当test.value为null时才会发生这种情况)如上所述,在您的示例中, 该值不为null,因此无需reflection即可确定此值

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

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

这三个function是:

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

  • 传递给在object声明的非虚方法的this值必须转换为object ; 因此,在值类型的对象的情况下,对GetType()的调用的接收器被装箱为object

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

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

问题是你假设test.valueTypefield.FieldType valueType相同,而不是。 获取test.valueType实际上获取存储在字段中的Type ,在本例中为0。

Type t = test.value.GetType()

是(在你的例子中)是一样的

Type t = 0.GetType()

为了演示,将value初始化为null, test.value.GetType()将抛出NullReferenceException

方法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 value) { Type result = Nullable.GetUnderlyingType(typeof (T)); return result; } } 

首先,您的示例中的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之前,在您开始使用不同类型时,最终会得到不同的结果。