【C# IO 操作 】Span<T>类
Span 原理探究
ref结构
ref struct是仅在堆栈上的值类型:
- 表现一个顺序结构的布局;(译注:可以理解为连续内存)
- 只能在堆栈上使用。即用作方法参数和局部变量;
- 不能是类或正常结构的静态或实例成员;
- 不能是异步方法或lambda表达式的方法参数;
- 不能动态绑定、装箱、拆箱、包装或转换。
ref struct也被称为嵌入式引用。
1. Span 源码分析
灵活运用 Span 解决工作中的实际问题我相信大家应该没什么毛病了,有了这个基础再从 Span 的源码 和 用户态 和大家一起深度剖析,从源码开始吧。
public readonly ref struct Span<T>
{
internal readonly ByReference<T> _pointer;
private readonly int _length;
}
上面代码的 ref struct
可以看出,这个 Span 是只可以分配在栈上的值类型,然后就是里面的 _pointer 和 _length 两个实例字段,不知道看完这两个字段脑子里是不是有一幅图,大概是这样的。
可以清晰的看出,Span 就是用来映射一段可以连续访问的内存地址,空间大小由 length 控制,开始位置由 _pointer 指定,是不是像极了指针,是的,语言团队要保证你的程序高性能,还得照护你的人身安全,出了各种手段,真是煞费苦心!
二、Span<T>常规用法
static void Main()
{ var nums = new int[] { 1, 2, 3, 4, 5, 6,8,9,7,10,11,12,13,14,15,16 };
//用数组来实例化span
var span = new Span<int>(nums); var Slice1 = span.Slice(1, 4);//2 3 4 5
var Slice2= span.Slice(10);// 11,12,13,14,15,16
var Slice3= Slice1.ToArray();//转化成数组
var Slice4 = new Span<int>(new int[Slice1.Length]);//新建一个span,长度不能小于slice1 要不会报错
Slice1.CopyTo(sp);
foreach (var item in Slice2)
{
Console.WriteLine($"{item}");
} }
三:Span 在 String 和 List 的实践
Span的应用场景真的是太多了,不可能在这篇一一列举,这里我就举两个例子吧,让大家能够感受到 Span 的强大即可。
1. 在 String 上的应用
案例:如何高效的计算出用户输入的值 10+20
?
1) 传统 Substring 做法
传统的做法很简单,截取呗,代码如下:
static void Main(string[] args)
{
var word = "10+20";
var splitIndex = word.IndexOf("+");
var num1 = int.Parse(word.Substring(0, splitIndex));
var num2 = int.Parse(word.Substring(splitIndex + 1));
var sum = num1 + num2;
Console.WriteLine($"{num1}+{num2}={sum}");
Console.ReadLine();
}
结果是很轻松的算出来了,但你仔细想想这里是不是有点什么问题,比如说为了从 word 中扣出 num,我用了两次 SubString,就意味着会在 托管堆 上生成两个 string,如果说我执行 1w 次话,那托管堆上会不会有 2w 个 string 呢? 修改代码如下:
for (int i = 0; i < 10000; i++)
{
var num1 = int.Parse(word.Substring(0, splitIndex));
var num2 = int.Parse(word.Substring(splitIndex + 1));
var sum = num1 + num2;
}
然后看一下 托管堆 上 String 的个数
0:000> !dumpheap -type String -stat
Statistics:
MT Count TotalSize Class Name
00007ffc53a81e18 20167 556538 System.String
托管堆上有 20167 个,挺恐怖的,真的是给 GC 添麻烦哈,这里还有 167 个是系统自带的,接下来的问题是有没有办法替换 SubString 从而不生成临时string呢?
2) 新式 Span 做法
如果看懂了 Span 结构图,你就应该会使用 _pointer + length 将 string 进行切片处理,对不对,代码如下:
for (int i = 0; i < 10000; i++)
{
var num1 = int.Parse(word.AsSpan(0, splitIndex));
var num2 = int.Parse(word.AsSpan(splitIndex));
var sum = num1 + num2;
}
然后在 托管堆 验证一下,是不是没有 临时 string 了?
0:000> !dumpheap -type String -stat
Statistics:
MT Count TotalSize Class Name
00007ffc53a51e18 167 36538 System.String
可以看到就只有 167 个系统字符串,性能也得到了不小的提升,。
2. 在 List 上的应用
平时用 Span 的时候,更多的会应用到 Array 上面,毕竟 Array 在托管堆上是连续内存,方便 Span 在上面画一个可视窗口,其实不仅仅是 Array,从 .NET5 开始在 List 上画一个视图也是可以的,截图如下:
因为 List 的 CURD 会导致底层的 Array 忽长忽短或重新分配,也就无法实现物理上的连续内存,所以 Span 应用到 List 之后,希望List是不可变的,这也是官方的建议。
四:总结
总的来说,Span 在 .NET 底层框架中的地位是越来越显著了,相信 netCore 追求更高更快的性能上 Span 一定大有可为,大家赶紧学起来,
原文链接:https://www.cnblogs.com/huangxincheng/p/13974476.html
【C# IO 操作 】Span<T>类的更多相关文章
- java的IO操作:System类对IO的支持。
目标: 1,掌握SYStem对IO的三种支持: system.out system.in system.err 2,掌握system.out及system.err的区别. 3,掌握输入,输出重定向. ...
- XML序列化 判断是否是手机 字符操作普通帮助类 验证数据帮助类 IO帮助类 c# Lambda操作类封装 C# -- 使用反射(Reflect)获取dll文件中的类型并调用方法 C# -- 文件的压缩与解压(GZipStream)
XML序列化 #region 序列化 /// <summary> /// XML序列化 /// </summary> /// <param name="ob ...
- Java的IO操作---File类
目标 1)掌握File类作用 2)可以使用file类中方法对文件进行读写操作. File类 唯一与文件有关的类.使用file类可进行创建或删除操作,要想使用File类,首先观察File类的构造方法. ...
- C++ IO操作API及注意事项(包含一个日志类的实现)
C++是一个抽象程度比C高很多的语言,在使用C++时,编译器做了很多工作,如果我们不对C++的某些特性的实现机制进行了解,那么编程时也许会有很多疑惑,我们也许知道怎样做才是正确的,但不知道为什么要这样 ...
- [19/04/04-星期四] IO技术_CommonsIO(通用IO,别人造的轮子,FileUtils类 操作文件 & IOUtilsl类 操作里边的内容 )
一.概念 JDK中提供的文件操作相关的类,但是功能都非常基础,进行复杂操作时需要做大量编程工作.实际开发中,往往需要 你自己动手编写相关的代码,尤其在遍历目录文件时,经常用到递归,非常繁琐. Apac ...
- C# IO操作(二)File类和Directory类的常用方法
本篇主要介绍一些常用的IO操作,对文件和目录的操作:留给自己复习之用. 1.创建文件 string sPath1=Path.GetDirectoryName(Assembly.GetExecuting ...
- [.NET] 利用 async & await 进行异步 IO 操作
利用 async & await 进行异步 IO 操作 [博主]反骨仔 [出处]http://www.cnblogs.com/liqingwen/p/6082673.html 序 上次,博主 ...
- 文件IO操作..修改文件的只读属性
文件的IO操作..很多同行的IO工具类都是直接写..但是如果文件有只读属性的话..则会写入失败..所以附加了一个只读的判断和修改.. 代码如下: /// <summary> /// 创建文 ...
- python之协程与IO操作
协程 协程,又称微线程,纤程.英文名Coroutine. 协程的概念很早就提出来了,但直到最近几年才在某些语言(如Lua)中得到广泛应用. 子程序,或者称为函数,在所有语言中都是层级调用,比如A调用B ...
- JAVASE02-Unit08: 文本数据IO操作 、 异常处理
Unit08: 文本数据IO操作 . 异常处理 * java.io.ObjectOutputStream * 对象输出流,作用是进行对象序列化 package day08; import java.i ...
随机推荐
- VUE3 之 组件传参
1. 概述 韦奇定律告诉我们:大部分人都很容易被别人的话所左右,从而开始动摇.怀疑,最终迷失自我.因此我们要努力的坚定信念,相信自己,才不会被周围的环境所左右,才能取得最终的胜利. 言归正传,之前我们 ...
- Spring Boot 3.0.0 发布第一个里程碑版本M1,你的 Java 升到17 了吗?
2022年1月20日,Spring官方发布了Spring Boot 3.0.0的第一个里程碑版本M1. 下面一起来来看看Spring Boot 3.0.0 M1版本都有哪些重大变化: Java基线从 ...
- Shell 脚本循环遍历日志文件中的值进行求和并计算平均值,最大值和最小值
本文为博主原创,转载请注明出处: 最近在进行压测,为了观察并定位服务性能的瓶颈,所以在代码中很多地方加了执行耗时的日志,但这种方式只能观察,却在压测的时候,不太能准确的把握代码中某些方法的性能,所以想 ...
- 聊一聊如何用C#轻松完成一个SAGA分布式事务
背景 银行跨行转账业务是一个典型分布式事务场景,假设 A 需要跨行转账给 B,那么就涉及两个银行的数据,无法通过一个数据库的本地事务保证转账的 ACID ,只能够通过分布式事务来解决. 市面上使用比较 ...
- python内置re模块全面实战
目录 一:取消转义 二:python内置模块之re模块 三:常用方法 findall search match 简便 四:常用方法 finditer 匹配文件多情况 五:切割 替换 内置模块 六:分组 ...
- 学习Java第9天
今天所作的工作: 反射,枚举类型与泛型 明天工作: 1.线程 2.网络通信 所遇到的问题及解决方法: 反射基本思想,泛型类似于类模板. 理解反射太难了,转悠了好半天,关键是理解反射的思想,才容易学.
- plsql 数据库事件触发器
--4.数据库事件触发器 需要超管的权限 /* 数据库事件触发器有数据库级和模式级两种. 前者定义在整个数据库上,触发事件是数据库事件,如数据库的启动.关闭,对数据库的登录或退出. 后者定义在模式上, ...
- HBase安装教程
一.版本介绍 linux : CentOS7 Hadoop : 2.7.6 zookeeper : 3.4.6 hbase : 1.4.6 jdk : jdk1.8.0_171 三个节点的主机名分别为 ...
- 自定义CALayer
1.如何自定义Layer. 自定义CALayer的方式创建UIView的方式非常相似. CALayer *layer = [CALayer layer]; layer.fr ...
- springboot实现分布式锁(spring integration,redis)
Springboot实现分布式锁(Spring Integration+Redis) 一.在项目的pom.xml中添加相关依赖 1)Spring Integration依赖 <dependenc ...