【问题标题】:Nullable type is not a nullable type?可空类型不是可空类型吗?
【发布时间】:2010-10-21 14:02:14
【问题描述】:

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

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?) 返回真正的可空类型?

【问题讨论】:

    标签: c# .net nullable gettype


    【解决方案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

    干杯!

    【讨论】:

    • 不能很好地扩展,是吗? Nullable、Nullable
    【解决方案2】:

    在 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 的 cmets 提示——这里有一个替代版本,它仅在 sourcenullNullable&lt;&gt; 时使用编译时类型;否则它使用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();
        }
    }
    

    【讨论】:

    • 请注意,这将获得 compile-time 类型。
    • @SLaks: 是否有过 nullable 的运行时类型与编译时类型不同的情况? (我很欣赏您可以将 any 类型传递给此方法,在这种情况下,您确实会获得运行时类型,但它的预期用途是获得可为空的“真实”类型,根据问题。)
    • 你是对的。 ((int?)2).GetRealType() 不算数。 ((object)nullable).GetRealType() 不明确。
    • @SLaks:糟糕,我的意思是“......在这种情况下,您确实会得到 compile-time 类型”。而且,是的,当您调用((object)nullable).GetRealType() 时,您根本不再传递可空值。您要么传递 nullable 的基础类型的装箱值,要么只是简单的 null。
    • 您可以提到 Nullable 变量不能被装箱以便与您的第二个版本一起使用。此外,将您的第一个版本重命名为 GetCompileType 可以清楚地说明此方法的真正作用。
    【解决方案3】:

    尽管 C# 假设​​值类型存储位置包含派生自 System.ValueType 的类型实例,而后者又派生自 System.Object,但这并不是真的。派生自System.ValueType 的每种类型实际上代表两种截然不同的事物:

    1. (对于原始类型)直接表示数据的字节集合,或(对于非原始结构类型)包含所有字段(公共和私有)的内容,但不包含任何类型信息。
    2. 一个独立的堆对象,除了上述之外还包含一个对象头,其类型派生自`System.ValueType`。

    值类型的存储位置是第一个;值类型的堆对象持有第二个。

    由于种种原因,微软决定Nullable&lt;T&gt;应该只支持第一次使用。如果尝试将Nullable&lt;T&gt; 类型的存储位置传递给需要对堆对象的引用的代码,则如果HasValue 为真,系统将把该项转换为T,否则,如果HasValue 是假的。虽然有多种方法可以创建 Nullable&lt;T&gt; 类型的堆对象,但将值类型存储位置转换为堆对象的常规方法永远不会生成。

    另请注意,在值存储位置调用GetType() 不会实际评估存储位置的类型,而是将该存储位置的内容转换为堆对象,然后返回结果对象的类型.因为Nullable&lt;T&gt; 类型的存储位置被转换为T 的对象实例或为null,所以对象实例中的任何内容都不会说明它来自的存储位置是否是Nullable&lt;T&gt;

    【讨论】:

      【解决方案4】:

      根据MSDN

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

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

      再次,来自MSDN

      装箱一个非空的可空值类型 框值类型本身,而不是 包装值的 System.Nullable 输入。

      【讨论】:

        猜你喜欢
        • 2011-03-10
        • 2017-09-25
        • 2021-02-17
        • 1970-01-01
        • 2020-12-19
        • 1970-01-01
        • 2022-09-27
        相关资源
        最近更新 更多