我对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
?
答案 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
之前,在您开始时使用的是不同的类型,最终会得到不同的结果。