String是一个奇怪的引用类型

开发 后端
string 被设计为不可变, 是因为 string在现代任何语言中,使用很频繁:多个对象可能都是这个字符面值, 然后就设计一个Pool来存储string。

[[402400]]

本文转载自微信公众号「精益码农」,作者小码甲 。转载本文请联系精益码农公众号。

开局两张图,内容全靠刷。

马甲哥看到这样的现象,一开始还是有点懵逼。

这个例子,string是纯粹的引用类型,但是在函数传值时类似于值传递;我之前给前后示例的内存变化图吧:

根因就是大多数高级语言都把String设计成不可变的: 由一个字符串池管理字符串面值。

为什么被设计成不可变。

这个我还真的搜索了一下[1]:

总结起来:

  • string 被设计为不可变, 是因为 string在现代任何语言中,使用很频繁:多个对象可能都是这个字符面值, 然后就设计一个Pool来存储string。

既然pool里面共享字符面值,修改的时候又不能影响到别人,那就只好重新拷贝产生新的字符面值。

  • 不可变资源消除了多线程中的资源竞争:对于文本的修改都会导致创建新空间,因此在多个线程同时访问文本无需设置锁,这对高频使用的String很友好。
  • 字符串不变性对于[在哈希表中使用字符串作为键]很友好,需要计算哈希值的对象必须是不可变的,以确保哈希值不变。

一个有意思的现象是:String虽然是引用类型,字符串对比时却表现的像值类型

  1. string str1="FooFoo"
  2. string strFoo="Foo"
  3. string str2= strFoo + strFoo; 
  4. return str1 == str2;           //  返回true 

正因为String不可变性 & Pool的机制,频繁变更字符串,会在池中产生很多临时的不用的字符串,所以我们有了优化的套路:

StringBuilder

代表可变的字符串,一旦修改不会尝试创建新对象,而是动态扩展内存

  1. var ss = new StringBuilder("Hello ", 100);  // 初次字符容量100 
  2. ss.Append("www.cnblogs.com"); 
  3. Console.WriteLine(ss.ToString());  // ss打印结果为:222 

Span

Span该出圈了,

Span提供对内存连续区域的类型安全访问,该内存可以位于堆、堆栈、甚至是非托管内存;

与String不可变性相关的是ReadOnlySpan(值类型), 提供内存数据的只读视图,每次切片不会产生新对象,而是在已存在的连续空间上创造新的视图。

  1. var text = "https://www.cnblogs.com/JulianHuang/p/14817621.html"
  2. ReadOnlySpan<char> nameSpan = text.AsSpan(8, 15); 
  3. nameSpan = nameSpan.Slice(4,7); 
  4. Console.WriteLine(nameSpan.ToString()); 

总结输出

今天从两张诡异的编程图聊到了String的不可变性、内存分布, 延伸谈到了

String不可变性的设计设计考量(有先射箭再画靶的嫌疑??)

针对频繁修改的String如何做内存优化

不是自吹,文章内容在业界相当硬核(多次被各大佬/CSDN点赞/转载),阅读和关注不是目的,更希望得到更多的阅读反馈,互相促进认知的提升(相当真诚??)。

参考资料

[1]这个我还真的搜索了一下: https://www.c-sharpcorner.com/UploadFile/230635/why-string-are-immutable-in-dotnet/

 

责任编辑:武晓燕 来源: 精益码农
相关推荐

2021-07-24 13:11:19

Redis数据技术

2009-08-27 16:39:26

C# String类型

2022-05-09 10:47:08

登录SpringSecurity

2022-08-11 12:28:09

For-elsePython

2021-08-11 07:53:22

Git rejecthint

2022-02-12 22:16:53

TypeScript类型字符串

2016-12-15 08:54:52

线程sessionopenSession

2021-08-02 08:21:53

Python编程语言开发

2020-04-20 09:02:33

函数RPCCPU

2022-03-07 05:53:41

线程CPU代码

2021-08-27 07:47:06

引用类型

2017-11-29 18:11:00

ERP管理数字化

2013-06-28 17:28:04

推送

2019-12-09 15:00:48

TomcatServlet容器

2012-05-10 16:32:01

Hadoop

2017-12-26 15:38:02

2023-01-30 09:01:34

DecoratorsJS语法

2012-08-23 14:23:33

函数式编程

2023-04-18 08:14:27

ElixirRustWebRTC

2023-03-13 08:09:03

Protobuffeature分割
点赞
收藏

51CTO技术栈公众号