可空类型不是可以为空的类型?

时间:2009-04-24 10:44:03

标签: c# .net nullable gettype

我正在使用可空类型进行一些测试,但它并没有像我预期的那样工作:

int? testInt = 0;
Type nullableType = typeof(int?);
Assert.AreEqual(nullableType, testInt.GetType()); // not the same type

这也不起作用:

DateTime? test = new DateTime(434523452345);
Assert.IsTrue(test.GetType() == typeof(Nullable)); //FAIL 

DateTime? test = new DateTime(434523452345);
Assert.IsTrue(test.GetType() == typeof(Nullable<>)); //STILL FAIL

我的问题是为什么testInt.GetType()返回int,而typeof(int?)返回真正的可空类型?

4 个答案:

答案 0 :(得分:56)

根据MSDN

  

在Nullable类型上调用GetType   导致拳击操作   当类型是隐式时执行   转换为Object。因此GetType   总是返回一个Type对象   代表基础类型,而不是   Nullable类型。

当你装入一个可以为空的对象时,只有底层类型被装箱。

再次,来自MSDN

  

装箱非空可空值类型   列出值类型本身,而不是   包装值的System.Nullable   类型。

答案 1 :(得分:22)

除了Romain的正确答案之外,如果你想比较“真实”类型(即,没有隐式地将任何可空类型转换为其基础类型),那么你可以创建一个类似的扩展方法:

public static class MyExtensionMethods
{
    public static Type GetRealType<T>(this T source)
    {
        return typeof(T);
    }
}

然后尝试以下测试:

int? a = 0;
Console.WriteLine(a.GetRealType() == typeof(int?));         // True
Console.WriteLine(a.GetRealType() == typeof(int));          // False

int b = 0;
Console.WriteLine(b.GetRealType() == typeof(int));          // True
Console.WriteLine(b.GetRealType() == typeof(int?));         // False

DateTime? c = DateTime.Now;
Console.WriteLine(c.GetRealType() == typeof(DateTime?));    // True
Console.WriteLine(c.GetRealType() == typeof(DateTime));     // False

DateTime d = DateTime.Now;
Console.WriteLine(d.GetRealType() == typeof(DateTime));     // True
Console.WriteLine(d.GetRealType() == typeof(DateTime?));    // False

修改...

为了完整性 - 并且由SLaks的评论提示 - 这是一个替代版本,当sourcenullNullable<>时,仅使用编译时类型;否则它使用GetType并返回运行时类型:

public static class MyExtensionMethods
{
    public static Type GetRealType<T>(this T source)
    {
        Type t = typeof(T);

        if ((source == null) || (Nullable.GetUnderlyingType(t) != null))
            return t;

        return source.GetType();
    }
}

答案 2 :(得分:3)

虽然C#假装值类型存储位置包含从System.ValueType派生的类型的实例,而System.Object又派生自System.ValueType,但事实并非如此。从Nullable<T>派生的每种类型实际上代表了两种截然不同的东西:

  1. 一组字节(对于基本类型)直接表示数据,或(对于非基本结构类型)保存所有字段(public和private)的内容,但不保存任何类型信息。
  2. 一个独立的堆对象,除了上面的内容之外还包含一个对象头,其类型派生自`System.ValueType`。

值类型的存储位置保持第一个;值类型的堆对象包含第二个。

出于各种原因,Microsoft决定Nullable<T>应仅支持第一次使用。如果尝试将类型为T的存储位置传递给期望引用堆对象的代码,则系统会在HasValue为真的情况下将项目转换为HasValue,否则如果Nullable<T>为false,则简单地传递空引用。虽然有一些方法可以创建类型为GetType()的堆对象,但是将值类型存储位置转换为堆对象的常规方法永远不会生成一个。

另请注意,在值存储位置调用Nullable<T>实际上不会评估存储位置的类型,而是将该存储位置的内容转换为堆对象,然后返回该类型的存储位置。结果对象。由于类型T的存储位置转换为Nullable<T>的对象实例或转换为null,因此对象实例中的任何内容都不会说明它所来自的存储位置是{{1}}。< / p>

答案 3 :(得分:1)

检查使用“is”运算符的简单方法:

(i is Nullable<int>) || (i is Nullable<long>) || (i is Nullable<float>) || (i is Nullable<short>)

我想到了读这两个MSDN页面:

http://msdn.microsoft.com/en-us/library/ms366789(v=vs.90).aspx

http://msdn.microsoft.com/en-us/library/ms228597%28v=VS.90%29.aspx

干杯!