使用 Nullable<T> 我们可以为原本不可能为 null 的值类型像引用类型那样提供一个 null 值。不过注意:Nullable<T> 本身也是个 struct,是个值类型哦。这意味着你随时可以调用 .HasValue 这样的方法,而不用担心会出现 NullReferenceException

等等!除了本文提到的一些情况。


Nullable 中的 null

注意看以下的代码。我们创建了一个值为 nullint?,然后依次输出 value 的值、value.GetType()

你觉得可以得到什么结果呢?

public class Program
{
public static void Main(string[] args)
{
int? value = GetValue(null); Console.WriteLine($"value = {value}");
Console.WriteLine($"type = {value.GetType()}");
Console.WriteLine($"TYPE = {typeof(int?)}"); Console.ReadLine();
} private static int? GetValue(int? source) => source;
}

结果是……

果是……

是……

……

崩掉了……

那么我们在 value 后面加个空传递运算符:

--  Console.WriteLine($"type  = {value.GetType()}");
++ Console.WriteLine($"type = {value?.GetType()}");

现在再次运行,我们确认了 value?.GetType() 的值为 null;而 typeof(int?) 的类型为 Nullable<Int32>

然而,我们现在将 value 的值从 null 改为 1

--  int? value = GetValue(null);
++ int? value = GetValue(1);

竟然 value.GetType() 得到的类型是 Int32

于是我们可以得出结论:

  1. 对于可空值类型,当为 null 时,GetType() 会出现空引用异常;
  2. 对于可空值类型,当不为 null 时,GetType() 返回的是对应的基础类型,而不是可空值类型;
  3. typeof(int?) 能够得到可空值类型。

Object.GetType() 和 is 对 Nullable 的作用

docs.microsoft.com 中,有一段对此的描述:

When you call the Object.GetType method on an instance of a nullable type, the instance is boxed to Object. As boxing of a non-null instance of a nullable type is equivalent to boxing of a value of the underlying type, GetType returns a Type object that represents the underlying type of a nullable type.

意思是说,当你对一个可空值类型 Nullable<T> 调用 Object.GetType() 方法的时候,这个实例会被装箱,会被隐式转换为一个 object 对象。然而对可空值类型的装箱与对值类型本身的装箱是同样的操作,所以调用 GetType() 的时候都是返回这个对象对应的实际基础类型。例如对一个 int? 进行装箱和对 int 装箱得到的 object 对象是一样的,于是 GetType() 实际上是不能区分这两种情况的。

那什么样的装箱会使得两个不同的类型被装箱为同一个了呢?

另一篇文档描述了 Nullable<T> 装箱的过程:

  • If HasValue returns false, the null reference is produced.
  • If HasValue returns true, a value of the underlying value type T is boxed, not the instance of Nullable.
  • 如果 HasValue 返回 false,那么就装箱一个 null
  • 如果 HasValue 返回 true,那么就将 Nullable<T> 中的 T 进行装箱,而不是 Nullable<T> 的实例。

这才是为什么 GetType() 会得到以上结果的原因。

同样的,也不能使用 is 运算符来确定这个类型到底是不是可空值类型:

Console.WriteLine($"value is int  = {value is int}");
Console.WriteLine($"value is int? = {value is int?}");

最终得到两者都是 True

应该如何判断可空值类型的真实类型

使用 Nullable.GetUnderlyingType(type) 方法,能够得到一个可空值类型中的基础类型,也就是得到 Nullable<T>T 的类型。如果得不到就返回 null

所以使用以下方法可以判断 type 的真实类型。

bool IsNullable(Type type) => Nullable.GetUnderlyingType(type) != null;

然而,这个 type 的实例怎么来呢?根据前面的示例代码,我们又不能调用 GetType() 方法。

实际上,这个 type 的实例就是拿不到,在运行时是不能确定的。我们只能在编译时确定,就像下面这样:

bool IsOfNullableType<T>(T _) => Nullable.GetUnderlyingType(typeof(T)) != null;

如果你是运行时拿到的可空值类型的实例,那么实际上此方法也是无能为力的。

public class Program
{
public static void Main(string[] args)
{
Console.Title = "walterlv's demo"; int? value = GetValue(1);
object o = value;
Console.WriteLine($"value is nullable? {IsOfNullableType(value)}");
Console.WriteLine($"o is nullable? {IsOfNullableType(o)}"); Console.ReadLine();
} private static int? GetValue(int? source) => source; static bool IsOfNullableType<T>(T _) => Nullable.GetUnderlyingType(typeof(T)) != null;
}


参考资料


我的博客会首发于 https://walterlv.com/,而 CSDN 和博客园仅从其中摘选发布,而且一旦发布了就不再更新。

本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。欢迎转载、使用、重新发布,但务必保留文章署名吕毅(包含链接:https://blog.csdn.net/wpwalter),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请与我联系

int? 竟然真的可以是 null!.NET/C# 确定可空值类型 Nullable 实例的真实类型的更多相关文章

  1. int不可为null引发的 MyBatis做持久层框架,返回值类型要为Integer问题

    MyBatis做持久层框架,返回值类型要为Integer MyBatis 做持久层时,之前没注意,有时候为了偷懒使用了int类型做为返回的类型,这样是不可取的,MyBatis做持久层框架,返回值类型要 ...

  2. 如果不空null并且不是空字符串才去修改这个值,但这样写只能针对字符串(String)类型,如果是Integer类型的话就会有问题了。 int i = 0; i!=''。 mybatis中会返回tr

    mybatis 参数为Integer型数据并赋值0时,有这样一个问题: mybatis.xml中有if判断条件判断参数不为空时,赋值为0的Integer参数被mybatis判断为空,因此不执行< ...

  3. 工作总结 无法确定条件表达式的类型,因为“<null>”和“System.DateTime”之间没有隐式转换 解决办法 object——Nullable<T> (可空类型)

    可空值类型 备注     一种类型认为是可以为 null,如果它可以分配一个值,也可以分配null,这意味着类型具有无论如何没有值. 默认情况下,所有都引用类型,如String,是否可以为 null, ...

  4. 2016年11月3日JS脚本简介数据类型: 1.整型:int 2.小数类型: float(单精度) double(双精度) decimal () 3.字符类型: chr 4.字符串类型:sting 5.日期时间:datetime 6.布尔型数据:bool 7.对象类型:object 8.二进制:binary 语言类型: 1.强类型语言:c++ c c# java 2.弱类型语

    数据类型: 1.整型:int 2.小数类型: float(单精度) double(双精度) decimal () 3.字符类型: chr 4.字符串类型:sting 5.日期时间:datetime 6 ...

  5. 关于 JavaScript 的 null 和 undefined,判断 null 的真实类型

    null.undefined 博客地址: https://ainyi.com/39 undefined:表示一个变量最原始的状态,而非人为操作的结果 null:表示一个对象被人为的重置为空对象,而非一 ...

  6. hdu_1014(竟然真的还有更水的)

    注意输出就没了... #include<cstdio> #include<cstring> using namespace std; int gcd(int a, int b) ...

  7. 你真的知道typeof null的结果为什么是‘object‘吗?

    到目前为止,ECMAScript 标准中定义了8种数据类型,它们分别是Undefined.Null.Number.Boolean.String.Symbol.BigInt.Object. 为了判断变量 ...

  8. Item 30 用enum代替int常量类型枚举,string常量类型枚举

    1.用枚举类型替代int枚举类型和string枚举类型 public class Show {   // Int枚举类型   // public static final int APPLE_FUJI ...

  9. null值可以赋给引用变量,不能给基本类型

    下面正确的写法是? cbyte i=128 boolean i=null long i=0xfffL double i=0.9239d null表示没有地址:null可以赋值给引用变量,不能将null ...

随机推荐

  1. TPCx-BB源码分析

    Copy from: 一篇文章看懂TPCx-BB(大数据基准测试工具)源码 TPCx-BB是大数据基准测试工具,它通过模拟零售商的30个应用场景,执行30个查询来衡量基于Hadoop的大数据系统的包括 ...

  2. Oracle数据库system用户忘记了密码怎么办

    1.在运行里面输入cmd调出dos窗口,然后在dos窗口中输入sqlplus /nolog 如:D:\oracle\ora92\bin>sqlplus /nolog 2.输入连接命令 如:SQL ...

  3. [Java学习] Java异常类型

    所有异常类型都是内置类Throwable的子类.因此,Throwable在异常类层次结构的顶层.紧接着Throwable下面的是两个把异常分成两个不同分支的子类.一个分支是Exception. 该类用 ...

  4. English trip -- Phonics 3 元音字母e

    xu言: 额...今天给我上自然拼读的maple老师 - . -和上次给我上第二集自然拼读的是同一个老师.突然考了考我上次学的内容~感觉大脑一片空白.看来review不能光说而不下苦功夫啊... 元音 ...

  5. ajax思维导图

  6. java.lang.RuntimeException: Unable to start activity ComponentInfo{com.autumn.book/com.autumn.book.MainActivity}: android.os.NetworkOnMainThreadException

    不能把http请求写在主线程里,改为这样 Runnable runnable = new Runnable() { public void run() { HttpClient.post2(" ...

  7. Professional layer CodeForces - 1103D (状压,gcd)

    大意: 给定$n$元素序列$a$, 现在想要让$gcd(a_1,a_2,...,a_n)=1$. 对于每个$a_i$可以除以一个不超过$k$的因子, 代价为$e_i$, 假设一共选择了$x$个元素去除 ...

  8. AND Graph CodeForces - 987F (状压)

    链接 大意:给定$m$个数, 若$x\&y=0$, 则在$x$与$y$之间连一条无向边. 求无向图的连通块个数 暴力连边显然超时的, 可以通过辅助结点优化连边, 复杂度$O(n2^n)$ #i ...

  9. Ubuntu18.04配置静态ip遇到的报错

    说明: 因为很多测试环境在这个Ubuntu系统上装着,却由于虚拟服务器的重启.断电,每次Ip发生变化,就得更新环境,所以需要把该环境的ip配置成静态的,一劳永逸. 根据以往配置Ubuntu静态ip的经 ...

  10. 转化为json方式函数

    1,我的数据格式是: {"message":"","code":0,"data":[{"Order" ...