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

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

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

  1. 面向过程编程
  2. 面向对象编程
  3. 面向函数式编程
  4. 面向异步编程
  5. 面向泛型编程

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

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

一:Span 是什么

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

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

public readonly ref struct Span<T>
    {
        internal readonly ByReference<T> _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<char> 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 <iostream>

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 所封装的底层指针玩法,我想这其实没什么难的,本篇就说到这里吧,希望对你有帮助。

责任编辑:武晓燕 来源: 一线码农聊技术
相关推荐

2023-09-11 08:20:17

对象闭包底层

2022-05-30 16:19:26

C#多态底层虚方法

2016-06-02 15:10:12

SwiftSelector

2022-02-14 08:04:02

Go语法糖编译器

2020-12-08 07:51:53

Java语法糖泛型

2009-08-19 15:38:59

C#代码

2021-01-30 11:12:21

C#List数据

2009-08-27 11:43:31

C#语法

2024-05-15 09:11:51

委托事件C#

2024-10-21 16:59:37

C#编程多线程

2024-09-11 16:34:38

语法糖Java语言

2016-10-14 14:04:34

JAVA语法main

2023-10-09 07:11:03

排序算法序列

2024-09-29 09:28:38

Action​C#

2009-08-18 12:52:33

C#枚举类型

2021-03-15 08:18:23

C#反射模块

2024-06-26 12:59:29

C#代码开发

2009-08-20 13:23:00

C#正则表达式

2009-08-25 17:59:49

C#入门

2019-05-23 11:42:04

Java语法糖编程语言
点赞
收藏

51CTO技术栈公众号