如果typeof(int?)是Int32?那么Nullable.GetUnderlyingType有什么用处?

18

typeof int?为什么是Int32

int? x = 1;
Console.WriteLine(x.GetType().Name);

如果一切正常,那么使用Nullable.GetUnderlyingType有什么用呢?


1
我认为问题在于代码为什么输出“Int32”而不是“Nullable<Int32>”。 - Martin Brown
1
我认为他的意思是:如果在上面的例子中调用x.GetType().Name可以得到Int32,那么我们为什么需要Nullable.GetUnderlyingType()来找出它是Int32 - decyclone
1
假设是错误的 - typeof(int?)System.Nullable<Int32> - IS4
5个回答

28

调用 GetType() 会将变量装箱。CLR有一个特殊规则,Nullable<T> 被装箱为 T。因此,x.GetType 将返回 Int32 而不是 Nullable<Int32>

int? x = 1;
x.GetType() //Int32
typeof(int?) //Nullable<Int32>

由于包含 nullNullable 会被装箱成 null,因此以下代码将抛出异常:

int? x = null;
x.GetType() //throws NullReferenceException

引用MSDN关于可空类型装箱的解释

基于可空类型的对象只有在对象非空时才会被装箱。如果HasValuefalse,则对象引用被赋值为null而不是被装箱。

如果对象非空——即HasValuetrue——那么装箱会发生,但只有基于可空对象的底层类型才会被装箱。对非空的可空值类型进行装箱会使值类型本身被装箱,而不是包装值类型的System.Nullable<T>被装箱。


@PeterLillevold 我认为这已经完成了(也许在你的评论之后),但无论如何,SO是一个协作环境 - 您可以随时编辑答案(或问题)并进行格式化。 - Ofer Zelig
1
@OferZelig 是的,我非常清楚。我想在2010年的时候我只是想给CodesInChaos一个机会来做这件事 :) - Peter Lillevold

11

这个例子有点混乱,因为:

int? x = 1;

创建一个Nullable<int>,就像您所期望的一样;然而:

Type type = x.GetType();

调用非虚方法object,这个方法并没有被重写,因此这是一个装箱操作;而Nullable<T>有特殊的装箱规则:

  • 如果它是空的,它会装箱成null
  • 如果它有一个值,这个会被装箱并返回

即:

int? x = 1;
int y = 1;

将方框内的内容完全转换为同样的东西。

因此,您正在将 typeof(int) 传递给 GetUnderlyingType

一个更具说明性的例子是在使用反射时:

class Foo {
    public int? Bar {get;set;}
}
...
Type type = typeof(Foo); // usually indirectly
foreach(var prop in type.GetProperties()) {
     Type propType = prop.PropertyType,
          nullType = Nullable.GetUnderlyingType(propType);

     if(nullType != null) {
         // special code for handling Nullable<T> properties;
         // note nullType now holds the T
     } else {
         // code for handling other properties
     }
}

4
"GetUnderlingType" - 我今天最喜欢的拼写错误。还有 "tupe". - BoltClock
你的意思是 x.GetType() 和 x.Value.GetType() 是一样的吗?很奇怪,不是吗?当你对返回可空类型的某个方法进行反射时,你会得到类似 Nullable'1 的东西。 - Mubashir Khan
@Mobashir:如果对象为null,则两者之间存在轻微差异。在前者中,对象将被装箱,并在生成的“null”引用上调用方法,这会导致运行时抛出NullReferenceException,而在后者中,Value属性(不需要装箱)将检查并在值确实为null时抛出InvalidOperationException - Mehrdad Afshari
还有一点,Nullable<T> 是一个结构体,但允许空赋值。 Nullable<int> x = null; - Mubashir Khan
@Mubashir 嗯,不完全是 - 它 假装 是这样的 - 但实际上是 C# 编译器为您更改了代码(变成了一些并非真正为 null 的东西)。对于涉及到 Nullable<T> 的大多数操作,它都会这样做。 - Marc Gravell
显示剩余7条评论

4

这是用于当你不知道它是Int32时的情况。

例子:

    public Type GetNullableUnderlyingType<T>(Nullable<T> obj) 
        where T : struct
    {
        return Nullable.GetUnderlyingType(typeof(Nullable<T>));
    }

在这里,您可以传递任何Nullable对象并使其返回底层类型。


1
这里的问题是,在普通代码中调用该方法时,泛型必须在调用方处解析。这意味着调用方已经知道类型。而且(为了匹配签名),他们必须已经知道它是否可为空等。如果他们已经知道...为什么还要询问呢? - Marc Gravell

2

当你写 int? 时,就好像你写了 Nullable<int>。我认为这是你要找的类型。


确切地说,它不应该像List<int>一样使用Nullable'1,而是应该使用类似于List'1的形式。 - Mubashir Khan

1
主要是用于处理通用方法: 例如:
public static void SomeMethod<T>(T argument)
{
     if(Nullable.GetUnderlyingType(typeof(T) != null)
     {
             /* special case for nullable code go here */
     }
     else
     {
            /* Do something else T isn't nullable */
     }
}

了解这一点非常重要,因为某些非常便宜的东西在可空类型上可能会非常昂贵。例如,if(argument == null)通常非常便宜,但在Nullable<T>上的通用方法中执行时,必须将argument装箱以获取空引用。您最好使用EqualityComparer<T>.Default,这会减慢其他所有操作,但使可空类型不受影响。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接