温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

C#可空类型原理是什么

发布时间:2021-12-21 10:36:45 阅读:134 作者:iii 栏目:开发技术
开发者测试专用服务器限时活动,0元免费领,库存有限,领完即止! 点击查看>>

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

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

        static void Main(string[] args)        {            int? num1 = 10;            int? num2 = null;            Console.WriteLine("执行结束啦!");            Console.ReadLine();        }

1. 挖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代码挖到这里就可以了。

2. 挖机器代码

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

     int? num1 = 10;     int? num2 =null;0:007> ~0sntdll!ZwReadFile+0x14:00007ffc`ec11aa64 c3              ret0:000> !clrstack -lOS Thread Id: 0x5364 (0)        Child SP               IP Call SiteConsoleApp4.Program.Main(System.String[]) [C:\dream\Csharp\ConsoleApp1\ConsoleApp4\Program.cs @ 21]    LOCALS:        0x00000018a9dfeaf8 = 0x0000000a00000001        0x00000018a9dfeaf0 = 0x000000000000000000000018a9dfed08 00007ffcd5b66c93 [GCFrame: 00000018a9dfed08] 

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

0:000> dd 0x00000018a9dfeaf8 
00000018`a9dfeaf8  00000001 0000000a a9dfec08 000000180: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。

三:有意外发现

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

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

2. 为什么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 -lOS 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 0x000000a8395feda8000000a8`395feda8  7fffffff 7fffffff 00007fff 00007fff000000a8`395fedb8  000000ff 000000ff 395feec8 000000a8000000a8`395fedc8  395fefc8 000000a8 395fee00 000000a8000000a8`395fedd8  d5b66c93 00007ffc 98e72d30 000001ee000000a8`395fede8  76504140 00007ffc 00000000 00000000000000a8`395fedf8  00000000 00007ffc 395feef0 000000a8000000a8`395fee08  971d0b20 000001ee 00000000 00000000000000a8`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 -lOS Thread Id: 0x23f8 (0)000000859a1fec60 00007ffc76630967 ConsoleApp4.Program.Main(System.String[]) [C:\dream\Csharp\ConsoleApp1\ConsoleApp4\Program.cs @ 32]    LOCALS:        0x000000859a1feca0 = 0x000002773cb32d70        0x000000859a1fec98 = 0x000002773cb32d90000000859a1feeb8 00007ffcd5b66c93 [GCFrame: 000000859a1feeb8] 
0:000> !do 0x000002773cb32d70Name:        System.Int32[]MethodTable: 00007ffcd2d58538EEClass:     00007ffcd2ec5918Size:        28(0x1c) bytesArray:       Rank 1, Number of elements 1, Type Int32 (Print Array)Fields:None0:000> !do 0x000002773cb32d90Name:        System.Nullable`1[[System.Int32, mscorlib]][]MethodTable: 00007ffcd3fb2058EEClass:     00007ffcd30221a0Size:        32(0x20) bytesArray:       Rank 1, Number of elements 1, Type VALUETYPE (Print Array)Fields:None0:000> !objsize 0x000002773cb32d70sizeof(000002773cb32d70) = 32 (0x20) bytes (System.Int32[])0:000> !objsize 0x000002773cb32d90sizeof(000002773cb32d90) = 32 (0x20) bytes (System.Nullable`1[[System.Int32, mscorlib]][])

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

C#可空类型原理是什么

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

亿速云「云服务器」,即开即用、新一代英特尔至强铂金CPU、三副本存储NVMe SSD云盘,价格低至29元/月。点击查看>>

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

原文链接:http://blog.itpub.net/69923331/viewspace-2692373/

AI

开发者交流群×