c#:Span结构体的使用

2024-05-15 18:18
文章标签 c# 使用 结构 .net netcore span

本文主要是介绍c#:Span结构体的使用,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

环境:

  • window10
  • .net core 3.1
  • vs2019

参考:
《.NET高性能编程 - C#如何安全、高效地玩转任何种类的内存之Span的本质(一)。》
《.NET高性能编程 - C#如何安全、高效地玩转任何种类的内存之Span的秉性特点(二)。》
《span之高性能字符串操作实测》

阅读前先补充基础知识:
《c#:值类型、引用类型、装箱和拆箱、结构体、readonly、ref》

一、为什么出现Span?

在Span出现之前:

  • 截取字符串使用方法:SubString(),但是这个方法会在内存中重新开辟空间并将字符串复制进去;
  • 需要访问数组一部分元素时,我们需要传递数组对象、起始索引、长度,如:stream.Write(new byte[100], 10, 20)

在有了Span之后,我们可以向下面这么用:

// 截取字符串示例,相比直接SubString,这里不再内存中开辟空间,也不拷贝字符串
string str = "一二三四五abcde12345";
var spanStr = str.AsSpan().Slice(10, 5);
var intRes = int.Parse(spanStr);
// 相比 “stream.Write(byteArr, 10, 20);”,我们不用传递起始索引和长度,而是将它作为整体作为入参
FileStream stream = null;
var byteArr = new byte[100];
var spanWrite = byteArr.AsSpan().Slice(10, 20);
stream.Write(spanWrite);

二、Span的概念

MSDN上面的解释是:提供任意内存的连续区域的类型和内存安全表示。

这里我的理解是:用Span封装一块连续内存地址的引用(注意:这块内存有数据类型),这样能避免不必要的数据拷贝。
在这里插入图片描述
正如上面图上所示,Span相当于封装了数组对象,要使用的数组起始索引、数组终止索引(或者是使用长度)。

三、Span可以操作的内存类型

span可以操作的内存类型包括:栈内存、托管堆内存、非托管堆内存等。

public static void Main()
{//托管堆内存var intarr = new int[100];Span<int> spanInt = new Span<int>(intarr);string str = "一二三四五abcde12345";var spanStr = str.AsSpan().Slice(10, 5);var intRes = int.Parse(spanStr);unsafe{// 栈内存byte* stackMem = stackalloc byte[100];Span<byte> span2 = new Span<byte>(stackMem, 100);}unsafe{// 非托管堆内存IntPtr unmanagedHeapMem = IntPtr.Zero;try{unmanagedHeapMem = Marshal.AllocHGlobal(100);Span<byte> span3 = new Span<byte>(unmanagedHeapMem.ToPointer(), 100);}catch{// 因为是非托管内存,所以必须手动释放Marshal.FreeHGlobal(unmanagedHeapMem);}}
}

四、Span应使用的场景

先来观察Span的定义:
在这里插入图片描述
看到 readonly ref,我们应该想到:

  • Span结构体是只读的(即:Span创建后它引用的对象及索引就确定了,不能再修改);

    注意:Span指向的数据是可以修改的。

  • Span结构体只能出现在栈中,不能出现在堆中;

    Span不能实现接口,不能用作类的属性,参考文章:
    《c#:值类型、引用类型、装箱和拆箱、结构体、readonly、ref》

那么,这就说明,Span只能出现在方法的局部变量、非异步(async/await)方法的参数里面。

下面是应用示例:
在这里插入图片描述

五、ReadOnlySpan

其实从上面的示例中也看到了ReadOnlySpan,相比Span,ReadOnlySpan限制了只能访问Span指向的数据。

这篇关于c#:Span结构体的使用的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/992561

相关文章

RabbitMQ 延时队列插件安装与使用示例详解(基于 Delayed Message Plugin)

《RabbitMQ延时队列插件安装与使用示例详解(基于DelayedMessagePlugin)》本文详解RabbitMQ通过安装rabbitmq_delayed_message_exchan... 目录 一、什么是 RabbitMQ 延时队列? 二、安装前准备✅ RabbitMQ 环境要求 三、安装延时队

C#文件复制异常:"未能找到文件"的解决方案与预防措施

《C#文件复制异常:未能找到文件的解决方案与预防措施》在C#开发中,文件操作是基础中的基础,但有时最基础的File.Copy()方法也会抛出令人困惑的异常,当targetFilePath设置为D:2... 目录一个看似简单的文件操作问题问题重现与错误分析错误代码示例错误信息根本原因分析全面解决方案1. 确保

Python ORM神器之SQLAlchemy基本使用完全指南

《PythonORM神器之SQLAlchemy基本使用完全指南》SQLAlchemy是Python主流ORM框架,通过对象化方式简化数据库操作,支持多数据库,提供引擎、会话、模型等核心组件,实现事务... 目录一、什么是SQLAlchemy?二、安装SQLAlchemy三、核心概念1. Engine(引擎)

Java Stream 并行流简介、使用与注意事项小结

《JavaStream并行流简介、使用与注意事项小结》Java8并行流基于StreamAPI,利用多核CPU提升计算密集型任务效率,但需注意线程安全、顺序不确定及线程池管理,可通过自定义线程池与C... 目录1. 并行流简介​特点:​2. 并行流的简单使用​示例:并行流的基本使用​3. 配合自定义线程池​示

基于C#实现PDF转图片的详细教程

《基于C#实现PDF转图片的详细教程》在数字化办公场景中,PDF文件的可视化处理需求日益增长,本文将围绕Spire.PDFfor.NET这一工具,详解如何通过C#将PDF转换为JPG、PNG等主流图片... 目录引言一、组件部署二、快速入门:PDF 转图片的核心 C# 代码三、分辨率设置 - 清晰度的决定因

C# LiteDB处理时间序列数据的高性能解决方案

《C#LiteDB处理时间序列数据的高性能解决方案》LiteDB作为.NET生态下的轻量级嵌入式NoSQL数据库,一直是时间序列处理的优选方案,本文将为大家大家简单介绍一下LiteDB处理时间序列数... 目录为什么选择LiteDB处理时间序列数据第一章:LiteDB时间序列数据模型设计1.1 核心设计原则

GO语言中函数命名返回值的使用

《GO语言中函数命名返回值的使用》在Go语言中,函数可以为其返回值指定名称,这被称为命名返回值或命名返回参数,这种特性可以使代码更清晰,特别是在返回多个值时,感兴趣的可以了解一下... 目录基本语法函数命名返回特点代码示例命名特点基本语法func functionName(parameters) (nam

使用shardingsphere实现mysql数据库分片方式

《使用shardingsphere实现mysql数据库分片方式》本文介绍如何使用ShardingSphere-JDBC在SpringBoot中实现MySQL水平分库,涵盖分片策略、路由算法及零侵入配置... 目录一、ShardingSphere 简介1.1 对比1.2 核心概念1.3 Sharding-Sp

Java 正则表达式的使用实战案例

《Java正则表达式的使用实战案例》本文详细介绍了Java正则表达式的使用方法,涵盖语法细节、核心类方法、高级特性及实战案例,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要... 目录一、正则表达式语法详解1. 基础字符匹配2. 字符类([]定义)3. 量词(控制匹配次数)4. 边

Python Counter 函数使用案例

《PythonCounter函数使用案例》Counter是collections模块中的一个类,专门用于对可迭代对象中的元素进行计数,接下来通过本文给大家介绍PythonCounter函数使用案例... 目录一、Counter函数概述二、基本使用案例(一)列表元素计数(二)字符串字符计数(三)元组计数三、C