C# 语法糖之聊聊 Span 的底层玩法

2023-09-01 10:07:17 来源:一线码农聊技术

把 Span 归于语法糖,可能有些偏了,但偏了就偏了,哈哈,只要是分享就好,C# 发展至今,已经是一门非常重的语言了,所有想要的它都要,即可以:

面向过程编程面向对象编程面向函数式编程面向异步编程面向泛型编程

既能做到高开发效率,又能做到高性能编程。

这里的Span就归结于高性能编程这个范畴了。


(资料图片)

一:Span 是什么

当年的 C# 一个亮点就是屏蔽了指针,自动内存托管,可以让程序员更加专注于业务,现如今策略变了,C# 要变得更加高性能,既然要做高性能那必然少不了指针,而指针又是面向托管层编程的程序员最怕的东西,所以就尽可能的封装,弄一套属于自己的托管指针玩法。

Span即属于托管指针玩法的一个典型代表,如果你用 ILSpy 去看它的struct结构,本质上就两个成员,一个叫_pointer,一个叫_length,参考如下代码:

public readonly ref struct Span    {        internal readonly ByReference _pointer;        private readonly int _length;    }

pointer 是指定起点, length 是控制边界,如果用 C 来模拟,大概就是这个样子。

struct Span {    void* ptr; int length;};

画个图大概就是这样子。

图片

二:Span 的场景在哪里

有了指针,就可以对内存进行原地操作,只要能原地操作,那就可以破掉语言层面上的诸多限制,实现接近C/C++级的高性能,有些朋友可能要问了,语言层面有什么限制?比如最典型的string,大家都知道string是一个writeoncopy特性的字符串,只要你动它一下,它就会繁殖,接下来我们就拿string举个例子。

1. string 中的数字求 sum

在很久以前你可能会这么做。

static void Main(string[] args)        {            var s = "97 3";            var arr = s.Split(new string[] { " " }, StringSplitOptions.RemoveEmptyEntries);            var sum = Convert.ToInt32(arr[0]) + Convert.ToInt32(arr[1]);            Console.WriteLine(sum);        }

图片

从代码可以看出,对 string 进行Split会导致生成多个小string对象,那有没有办法不用生成小string呢?这就需要用到托管版的Span做原地处理了。

static void Main(string[] args)        {            var s = "97 3";            var position = s.IndexOf(" ");            ReadOnlySpan span = s.AsSpan();            var num1 = int.Parse(span.Slice(0, position));            var num2 = int.Parse(span.Slice(position));            Console.WriteLine(num1 + num2);        }

图片

Span的这种做法就是通过_pointer指针在内存地址上进行移动来完成,如果看不明白,我可以用C来模拟一下。

#include struct Span { int length; void* ptr;};void sum(Span* span);int main(){ Span span; span.ptr = (char*)"97 3"; span.length = strlen((char*)span.ptr); sum(&span);}void sum(Span* span) { int sum = 0; char* position = strchr((char*)span->ptr, " "); Span span1; span1.ptr = span->ptr; span1.length = (position - span->ptr) / sizeof(char); Span span2; span2.ptr = position; span2.length = span->length - span1.length - 1; int num1= atoi((char*)span1.ptr); int num2= atoi((char*)span2.ptr); sum = num1 + num2; printf("sum=%d", sum);}

图片

虽然代码有点多,但逻辑还是很清楚的。

如果大家明白Span所封装的底层指针玩法,我想这其实没什么难的,本篇就说到这里吧,希望对你有帮助。

标签:

推荐阅读>