返回顶部
首页 > 资讯 > 精选 >C#可空类型原理是什么
  • 419
分享到

C#可空类型原理是什么

2023-06-03 17:06:22 419人浏览 薄情痞子
摘要

这篇文章主要介绍“C#可空类型原理是什么”,在日常操作中,相信很多人在C#可空类型原理是什么问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”C#可空类型原理是什么”的疑惑有所帮助!接下来,请跟着小编一起来学习吧

这篇文章主要介绍“C#可空类型原理是什么”,在日常操作中,相信很多人在C#可空类型原理是什么问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”C#可空类型原理是什么”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!

为了方便演示,我就定义一个int?类型,接收非null和null两种情况。


       static void Main(string[] args)
       {
           int? num1 = 10;
           int? num2 = null;

           Console.WriteLine("执行结束啦!");
           Console.ReadLine();
       }

挖IL代码

挖IL代码简单,用ILSPY小工具就可以了,编译后生成的IL代码如下:


.method private hidebysig static
void Main (
string[] args
) cil managed
{
// Method begins at RVA 0x2048
// Code size 36 (0x24)
.maxstack 2
.entrypoint
.locals init (
[0] valuetype [mscorlib]System.Nullable`1<int32> num1,
[1] valuetype [mscorlib]System.Nullable`1<int32> num2
)

IL_0000: nop
IL_0001: ldloca.s 0
IL_0003: ldc.i4.s 10
IL_0005: call instance void valuetype [mscorlib]System.Nullable`1<int32>::.ctor(!0)
IL_000a: ldloca.s 1
IL_000c: initobj valuetype [mscorlib]System.Nullable`1<int32>
IL_0012: ldstr "执行结束啦!"
IL_0017: call void [mscorlib]System.Console::WriteLine(string)
IL_001c: nop
IL_001d: call string [mscorlib]System.Console::ReadLine()
IL_0022: pop
IL_0023: ret
} // end of method Program::Main

这IL代码还是非常易懂的,比汇编简单多啦(┬_┬),可以看到int ? 就是 System.Nullable<int32> ,然后从valuetype 标记可以看到这玩意是个值类型,所以把上面的代码回转成C#代码就是下面这样。


   {
       static void Main(string[] args)
       {
           //int? num1 = 10;
           //int? num2 = null;

           Nullable<int> num3 = new Nullable<int>(10);
           Nullable<int> num4 = new Nullable<int>();

           Console.WriteLine("执行结束啦!");
           Console.ReadLine();
       }

很简单吧,那怎么输出num3和num4呢? 直接Console.WriteLine就好了。

C#可空类型原理是什么

这里你肯定有一个疑问,为什么num3输出10,而num4什么都没输出呢? 哈哈,这是因为Nullable的ToString()被重写了,再来看下ToString被重写成啥样了,代码如下:

public struct Nullable<T> where T : struct
{
  private bool hasValue;

  internal T value;

  [NonVersionable]
  [__DynamicallyInvokable]
  public Nullable(T value)
  {
  this.value = value;
  hasValue = true;
  }

  [__DynamicallyInvokable]
  public override string ToString()
  {
  if (!hasValue)
  {
  return "";
  }
  return value.ToString();
  }
}

可以看到ToString方法里要么返回空字符串要么返回你在构造函数中塞入的value,这这么简单,IL代码挖到这里就可以了。

挖机器代码

要看num1和num2的机器代码,其实也就是看 Nullable<T> 的内存布局方式,这里我使用windbg,还是使用 !clrstack -l 查看线程栈。


    int? num1 = 10;
    int? num2 =null;

0:007> ~0s
ntdll!ZwReadFile+0x14:
00007ffc`ec11aa64 c3              ret
0:000> !clrstack -l
OS Thread Id: 0x5364 (0)
       Child SP               IP Call Site
ConsoleApp4.Program.Main(System.String[]) [C:\dream\Csharp\ConsoleApp1\ConsoleApp4\Program.cs @ 21]
   LOCALS:
       0x00000018a9dfeaf8 = 0x0000000a00000001
       0x00000018a9dfeaf0 = 0x0000000000000000

00000018a9dfed08 00007ffcd5b66c93 [GCFrame: 00000018a9dfed08]

从LOCALS中可以看到,num1和num2的线程栈上存放的内容分别是0x0000000a00000001和 0x0000000000000000, 不过这值也挺奇怪的,一个是1一个是0。。。我们用 dd 命令把地址转储出来。


0:000> dd 0x00000018a9dfeaf8
00000018`a9dfeaf8  00000001 0000000a a9dfec08 00000018
0:000> dd 0x00000018a9dfeaf0
00000018`a9dfeaf0  00000000 00000000 00000001 0000000a

C#可空类型原理是什么

在num1的内存区域中有一个十六进制值 0000000a ,这就是十进制的10,那前面的 00000001 是什么东西呢? 大家不要忘啦, int?是语法糖, 你现在看的是 Nullable<T> 哈。。。

C#可空类型原理是什么

看清楚啦,这个结构体里面有两个值类型字段,自然 00000001 就是 hasValue=true啦。 num2也就好理解了,两个默认值也就是两个0了。00000000 00000000。

三:有意外发现

int? 比 int 要占用更多的内存

如果你的内存数据量特别大的话,你就要当心了,int? 比 int 在x64上要多占4个字节,也就是多一倍,无论线程栈还是托管堆。

为什么bool要占用 4字节空间?

<1> 线程栈上的演示

肯定有人比较疑惑,bool在C#中不就是一个字节嘛? 你怎么说是4个字节呢? 你要是问我,我只能说从windbg上看就是这样的,x64系统的线程栈上就是以4个字节为一个单位,你不信的话,我就在代码中定义不同字段的 值类型,你看看在线程栈上的分布不就好啦,以事实说话。

           byte b1 = byte.MaxValue;
          byte b2 = byte.MaxValue;
          short b3 = short.MaxValue;
          short b4 = short.MaxValue;
          int b5 = int.MaxValue;
          int b6 = int.MaxValue;

0:000> !clrstack -l
OS Thread Id: 0xa98 (0)
ConsoleApp4.Program.Main(System.String[]) [C:\dream\Csharp\ConsoleApp1\ConsoleApp4\Program.cs @ 25]
  LOCALS:
      0x000000a8395fedbc = 0x00000000000000ff
      0x000000a8395fedb8 = 0x00000000000000ff
      0x000000a8395fedb4 = 0x0000000000007fff
      0x000000a8395fedb0 = 0x0000000000007fff
      0x000000a8395fedac = 0x000000007fffffff
      0x000000a8395feda8 = 0x000000007fffffff

然后把最小的地址0x000000a8395feda8 转储出来。


0:000> dd 0x000000a8395feda8
000000a8`395feda8  7fffffff 7fffffff 00007fff 00007fff
000000a8`395fedb8  000000ff 000000ff 395feec8 000000a8
000000a8`395fedc8  395fefc8 000000a8 395fee00 000000a8
000000a8`395fedd8  d5b66c93 00007ffc 98e72d30 000001ee
000000a8`395fede8  76504140 00007ffc 00000000 00000000
000000a8`395fedf8  00000000 00007ffc 395feef0 000000a8
000000a8`395fee08  971d0b20 000001ee 00000000 00000000
000000a8`395fee18  d5b66b79 00007ffc 00000000 00000000

对比一下可以看到上面的 7fffffff, 00007fff,000000ff就是相应的int,short,byte的MaxValue, 都是占用4个字节的空间,没问题吧。

<2> 托管堆演示


   var arr1 = new int[] { 10 };
   var arr2 = new int?[] { 14 };

0:000> !clrstack -l
OS Thread Id: 0x23f8 (0)
000000859a1fec60 00007ffc76630967 ConsoleApp4.Program.Main(System.String[]) [C:\dream\Csharp\ConsoleApp1\ConsoleApp4\Program.cs @ 32]
   LOCALS:
       0x000000859a1feca0 = 0x000002773cb32d70
       0x000000859a1fec98 = 0x000002773cb32d90

000000859a1feeb8 00007ffcd5b66c93 [GCFrame: 000000859a1feeb8]
0:000> !do 0x000002773cb32d70
Name:        System.Int32[]
MethodTable: 00007ffcd2d58538
EEClass:     00007ffcd2ec5918
Size:        28(0x1c) bytes
Array:       Rank 1, Number of elements 1, Type Int32 (Print Array)
Fields:
None
0:000> !do 0x000002773cb32d90
Name:        System.Nullable`1[[System.Int32, mscorlib]][]
MethodTable: 00007ffcd3fb2058
EEClass:     00007ffcd30221a0
Size:        32(0x20) bytes
Array:       Rank 1, Number of elements 1, Type VALUETYPE (Print Array)
Fields:
None

0:000> !objsize 0x000002773cb32d70
sizeof(000002773cb32d70) = 32 (0x20) bytes (System.Int32[])
0:000> !objsize 0x000002773cb32d90
sizeof(000002773cb32d90) = 32 (0x20) bytes (System.Nullable`1[[System.Int32, mscorlib]][])

可以看到,一个是28byte,一个是32byte,多出来的就是那个hasValue哈,有一点要注意了,用!objsize打出来都是32byte,这是因为28byte要靠8对齐就变成32byte啦, 然后我把两个值类型转储出来,如下图:

C#可空类型原理是什么

到此,关于“C#可空类型原理是什么”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注编程网网站,小编会继续努力为大家带来更多实用的文章!

--结束END--

本文标题: C#可空类型原理是什么

本文链接: https://lsjlt.com/news/235061.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • C#可空类型原理是什么
    这篇文章主要介绍“C#可空类型原理是什么”,在日常操作中,相信很多人在C#可空类型原理是什么问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”C#可空类型原理是什么”的疑惑有所帮助!接下来,请跟着小编一起来学习吧...
    99+
    2023-06-03
  • 如何理解C#可空值类型
    如何理解C#可空值类型,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。通常一个值类型变量永远不可能为null,它总是包含值类型的值本身。但是在某些情况下会遇到一些问题如:在映...
    99+
    2023-06-17
  • c#中String类型的存储原理是什么
    这篇文章主要介绍了c#中String类型的存储原理是什么的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇c#中String类型的存储原理是什么文章都会有所收获,下面我们一起来看看吧。在我们正式了解c#中的Stri...
    99+
    2023-06-29
  • C#可空引用类型怎么使用
    本篇内容主要讲解“C#可空引用类型怎么使用”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“C#可空引用类型怎么使用”吧!安装您必须下载Visual Studio 2017 15.5预览版(目前最新...
    99+
    2023-06-29
  • C#的可空数据类型怎么使用
    这篇文章主要介绍“C#的可空数据类型怎么使用”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“C#的可空数据类型怎么使用”文章能帮助大家解决问题。C# 可空类型(Nullable)C# 提供了一个特殊的...
    99+
    2023-06-17
  • C#新特性之可空引用类型
    安装 您必须下载Visual Studio 2017 15.5预览版(目前最新发布版本是15.4),下载地址:https://www.visualstudio.com/en-us/n...
    99+
    2024-04-02
  • MySQL锁类型和加锁原理是什么
    这篇文章主要介绍了MySQL锁类型和加锁原理是什么,具有一定借鉴价值,需要的朋友可以参考下。希望大家阅读完这篇文章后大有收获。下面让小编带着大家一起了解一下。首先对mysql锁进行划分:按照锁的粒度划分:行...
    99+
    2024-04-02
  • java中枚举类型的原理是什么
    java中枚举类型的原理是什么?针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。Java的特点有哪些Java的特点有哪些1.Java语言作为静态面向对象编程语言的代表,实现了面...
    99+
    2023-06-14
  • C#基元类型、值类型、引用类型是什么
    这篇文章主要讲解了“C#基元类型、值类型、引用类型是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“C#基元类型、值类型、引用类型是什么”吧!首先了解下什么是基元类型,基元类型是编译器直接...
    99+
    2023-06-17
  • C#中泛型的运作原理是什么
    这篇文章给大家介绍C#中泛型的运作原理是什么,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。一.泛型之前的故事#我们肯定会想到用object来作为类型参数,因为在C#中,所有类型都是基于Object类型的。因此Objec...
    99+
    2023-06-07
  • Redis源码类型的实现原理是什么
    这期内容当中小编将会给大家带来有关Redis源码类型的实现原理是什么,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。  Redis源码类型的实现原理有什么  Redis内部...
    99+
    2024-04-02
  • 常见SQL注入类型及原理是什么
    这篇“常见SQL注入类型及原理是什么”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“常见SQ...
    99+
    2024-04-02
  • 云服务器技术原理是什么类型
    云服务器(Cloud Drive)是一种基于云计算技术的服务器,它通过网络将计算机和应用程序连接起来,使其可以使用互联网上的公共资源来执行任务,例如网站托管、数据备份、虚拟主机和云存储等。 云服务器提供了一种远程托管的方式,使得用户可以通...
    99+
    2023-10-26
    原理 类型 服务器
  • C#的对象类型是什么
    本文小编为大家详细介绍“C#的对象类型是什么”,内容详细,步骤清晰,细节处理妥当,希望这篇“C#的对象类型是什么”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。对象(Object)类型对象(Object)类型&nb...
    99+
    2023-06-17
  • JavaScript原型链的原理是什么
    这篇文章主要讲解了“JavaScript原型链的原理是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“JavaScript原型链的原理是什么”吧!解析原型...
    99+
    2024-04-02
  • MySQL中索引类型的实现原理是什么
    本篇文章给大家分享的是有关MySQL中索引类型的实现原理是什么,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。  一、按表列属性分类:  1.单...
    99+
    2024-04-02
  • 云服务器技术原理是什么类型的
    云服务器(Cloud Direct)是一种用于存储和共享数据的服务器技术,通过虚拟化技术为用户提供快速、稳定、低成本的云存储和共享服务。常见的云服务器有VMware、AWS和阿里云。 云服务器的技术原理如下: 服务器虚拟化技术:云服务器...
    99+
    2023-10-27
    原理 类型 服务器
  • Go 中函数类型的底层原理是什么?
    go 中的函数类型是一个具有输入参数类型和输出返回类型的元组。函数类型可以作为值或引用传递,默认情况下作为值传递,显式作为引用传递需要使用 *。在实战中,函数类型可用于创建可重用的函数,...
    99+
    2024-04-19
    底层原理 函数类型
  • C语言中可变参数的原理是什么
    C语言中可变参数的原理是什么,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。var_list可变参数介绍VA_LIST 是在C语言中解决变参问题的一组宏,原型:typedef&n...
    99+
    2023-06-15
  • c++中的switch可以判断什么类型
    c++ 中 switch 语句可以判断整数、字符和枚举类型的表达式。工作原理是根据表达式值转移控制流到匹配的分支,未匹配则执行 default 分支。语法为:switch (开关表达式)...
    99+
    2024-05-06
    c++
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作