6

我对以下类型的行为感到困惑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不使用反射?

4

5 回答 5

11

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

这种行为是不受欢迎和不幸的;这是由于三个特征组合在一起的行为,这些特征本身就表现得很合理。

这三个特点是:

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

  • this传递给声明的非虚拟方法的值object必须转换为object; 因此,对于值类型的对象,调用的接收者GetType()被装箱到object.

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

每个功能本身都是合理的,但结合起来结果是不可取的:当您调用GetType有效的可为空的 int 时,运行时将可为空的 int 装箱到装箱的 int 中,然后将其作为当然报告this的那个传递。如果该值为 null 可为 null 的 int,则运行时框为 null,然后在 null 引用上调用并崩溃。这些行为都不是可取的,但我们被他们困住了。object.GetTypeintGetType

于 2013-05-05T14:14:40.430 回答
8

可空类型有点奇怪。然而,至少他们的行为是有据可查的。

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

您还可以使用 System.Reflection 命名空间的类和方法来生成表示 Nullable 类型的 Type 对象。但是,如果您尝试在运行时使用 GetType 方法或 is 运算符从 Nullable 变量中获取类型信息,则结果是表示基础类型的 Type 对象,而不是 Nullable 类型本身。<br />
对 Nullable 类型调用 GetType 会导致在类型隐式转换为 Object 时执行装箱操作。因此 GetType 总是返回一个表示基础类型的 Type 对象,而不是 Nullable 类型。

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

另外,要明确回答问题的最后一部分:在不使用反射的情况下判断 test.value 可以为空的唯一方法是访问它并获取 NullReferenceException(当然,只有在 test.value 为 null 时才会发生这种情况. 如所写,在您的示例中,该值不为空,因此在没有反射的情况下确定这一点是不可能的

于 2013-05-05T06:31:08.473 回答
3

问题是您假设Typeof与oftest.value相同,但事实并非如此。获取of实际上获取存储在字段中的内容,在本例中为 0。 Typefield.FieldTypevalueTypetest.valueType

Type t = test.value.GetType()

与(在您的示例中)相同

Type t = 0.GetType()

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

于 2013-05-05T06:29:05.273 回答
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;
    }
}
于 2013-05-05T06:32:26.763 回答
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()对一个字段做 a 时,你会得到里面数据的类型,而不是字段本身的类型。例如:

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

在这个例子中,当你调用 时GetType(),你也会得到System.Int32,而不是System.Object。因为在你开始调用之前使用不同的类型GetUnderylingType,你最终会得到不同的结果。

于 2013-05-05T07:45:29.010 回答