c#基础系列之值类型和引用类型的深入理解

发布时间:2020-08-21 13:19:28 作者:大菜
来源:脚本之家 阅读:264

前言

不知不觉已经踏入坑已10余年之多,对于c#多多少少有一点自己的认识,写出来渴求同类抨击,对自己也算是个十年之痒的一个总结。

C#把数据类型分为值类型和引用类型

1.1:从概念上来看,其区别是值类型直接存储值,而引用类型存储对值的引用。

1.2:这两种类型在内存的不同地方,值类型存储在堆栈中,而引用类型存储在托管对上。存储位置的不同会有不同的影响。

下面话不多说了,来一起看看详细的介绍吧

基本概念

CLR支持两种类型:值类型和引用类型。 面试过很多5年左右的同学,有很多连值类型和引用类型的基本概念都回答不上来,难道现在的c#开发人员基础这么弱了吗?还是大家都不重视基础呢?这个随便找一篇博客都可以基础入门的。

c#基础系列之值类型和引用类型的深入理解

引用类型

哪些类型是引用类型呢?其实一个可以称为”类“的类型都是引用类型。 引用类型总是从托管堆上分配的,常用的语法就是New XX(). C#的new 操作符会返回对象的指针 - 也就是指向对象数据的内存地址的一个引用。引用类型的传递其实传递的是对象的指针(string类型比较特殊),所以在特定的场景下性能是高于值类型的。一个引用类型在创建时默认为null,也就是说当前变量不指向一个有效的对象,也就是我们常遇到的异常“未将对象引用设置到对象的实例”。

值类型

因为引用类型变量都需要进行一次堆内存的分配,这会给GC造成很大的压力,所以CLR提供了轻量级类型“值类型”。 值类型一般在线程栈上分配。(注意:值类型可以嵌入一个引用对象中)一个值类型变量其实就包含了值类型实例的值,所以它没有引用类型的指针(大家猜想值类型需不需要类型对象指针呢?)

相同点和不同点

相同点

 interface Itest
 {
 void test();
 }
 struct TestStruct : Itest
 {
 public void test()
 {
 throw new NotImplementedException();
 }
 }

不同点

c#基础系列之值类型和引用类型的深入理解

性能

有的同学说值类型的性能高于引用类型,那为什么不都用值类型呢?引用类型也是如此。任何东西都有两面性,只有合适的类型,没有万能的类型。

1、值类型:所谓的.net Framework中的“轻量类型”,为什么说是“轻量”呢,这和他的内存分配有直接关系,因为值类型是分配在栈上,所以在GC的控制之外,不会对GC造成压力。那是不是可以随便用呢?当然不是,举个例子:我自定义一个struct 类型作为一个方法的参数会发生什么呢?每次调用都会发生全字段的赋值,这是不可接受的,这也是典型的值类型勿用场景。

2、引用类型:引用类型分配在堆中,所以会影响GC,如果频繁的初始化引用类型,对GC的压力是很大的,因为每一次分配都有可能会强制执行一次垃圾收集操作。另外提一点,引用类型的所占内存,并非所有属性/字段的和,堆上分配的每个对象都有一些额外的成员,这些成员必须初始化。(类型对象指针和内存块索引)。

3、装箱拆箱:所谓装箱就是将值类型转化为引用类型的过程。拆箱则相反(只是概念上相反,实际编译器的操作不一样)。有的同学说装箱拆箱影响性能,那到底是装箱影响呢还是拆箱呢还是都影响呢?

装箱发生了什么过程呢:

拆箱发生了什么过程呢:

所以装箱是比较耗费性能的,还有可能引发一次GC操作,而拆箱只是一个获取指针的过程耗费资源要比装箱小的多。注意:一个对象拆箱之后只能还原为原先未装箱之前的类型,例如:你不能把int32类型装箱后还原为int16类型。 所以面试的时候可以和面试官装B一下了~~

c#基础系列之值类型和引用类型的深入理解

测试例子

值类型引用类型分别初始化N次消耗的时间,代码如下

static void Main(string[] args)
 {
 Console.WriteLine("test start");
 int totalCount = 10000000;
 Stopwatch sw = new Stopwatch();
 sw.Start();
 for (int i = 0; i < totalCount; i++)
 {
  TestRef temp = new TestRef() { Id = i, Name = "test" };
 }
 sw.Stop();
 Console.WriteLine($"引用类型耗时:{sw.ElapsedMilliseconds}");
 sw.Reset();
 sw.Start();

 for (int i = 0; i < totalCount; i++)
 {
  TestVal temp = new TestVal() { Id = i, Name = "test" };
 }
 sw.Stop();
 Console.WriteLine($"值类型耗时:{sw.ElapsedMilliseconds}");
 Console.Read();
 }

 class TestRef
 {
 public int Id { get; set; }
 public string Name { get; set; }
 }
 struct TestVal
 {
 public int Id { get; set; }
 public string Name { get; set; }
 }

运行结果:

引用类型耗时:205
值类型耗时:152

可见初始化速度值类型是优于引用类型的,也可能是引用类型引发了GC导致。

作为方法参数传递,代码如下:

static void Main(string[] args)
 {
 Console.WriteLine("test start");
 long totalCount = 1000000000;
 Stopwatch sw = new Stopwatch();
 sw.Start();

 TestRef tempRef = new TestRef() { Id = 1, Name = "test" , Name2="r3rewfdsafdsa", Name3="fsrewfdsafdsafdsa", Name4="fdafdasfdsafdsa", Name5="432tretsfds", Name6="fdsafdasfdasfd" };
 for (int i = 0; i < totalCount; i++)
 {
  TestR(tempRef);
 }
 sw.Stop();
 Console.WriteLine($"引用类型耗时:{sw.ElapsedMilliseconds}");
 sw.Reset();
 sw.Start();
 TestVal tempVal = new TestVal() { Id = 1, Name = "test", Name2 = "r3rewfdsafdsa", Name3 = "fsrewfdsafdsafdsa", Name4 = "fdafdasfdsafdsa", Name5 = "432tretsfds", Name6 = "fdsafdasfdasfd" };
 for (int i = 0; i < totalCount; i++)
 {
  TestV(tempVal);
 }
 sw.Stop();
 Console.WriteLine($"值类型耗时:{sw.ElapsedMilliseconds}");
 Console.Read();
 }
 static void TestR(TestRef r)
 {
 return;
 }
 static void TestV(TestVal v)
 {
 return;
 }

 class TestRef
 {
 public int Id { get; set; }
 public string Name { get; set; }
 public string Name2 { get; set; }
 public string Name3 { get; set; }
 public string Name4 { get; set; }
 public string Name5 { get; set; }
 public string Name6 { get; set; }
 }
 struct TestVal
 {
 public int Id { get; set; }
 public string Name { get; set; }
 public string Name2 { get; set; }
 public string Name3 { get; set; }
 public string Name4 { get; set; }
 public string Name5 { get; set; }
 public string Name6 { get; set; }
 }

运行结果:

引用类型耗时:4437
值类型耗时:5226

可见在普通情况下,作为参数值类型和引用类型用时差距不大,但是,如果值类型的实例属性比较多的情况下差距降进一步拉大。

非正式环境测试用例,结果仅供参考

应用场景

不止是面试的时候经常问应用场景这个问题,就是自己平时写程序也应该清楚。程序设计选择的时候大部分场景都是用引用类型,但是如果你满足下列条件,值类型可能更适用:

其他

顺便说一句,好久不写博客,样式真实花时间啊,后来干脆写markdown格式的,请大家见谅!!

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对亿速云的支持。

推荐阅读:
  1. 引用类型和值类型的比较
  2. 细说C#中的值类型和引用类型

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

值类型 引用类型

上一篇:VUE axios发送跨域请求需要注意的问题

下一篇:Android更多条目收缩展开控件ExpandView的示例代码

相关阅读

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

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