C# 語法糖之聊聊 Span 的底層玩法
把 Span 歸于語法糖,可能有些偏了,但偏了就偏了,哈哈,只要是分享就好,C# 發(fā)展至今,已經(jīng)是一門非常重的語言了,所有想要的它都要,即可以:
- 面向過程編程
- 面向?qū)ο缶幊?/li>
- 面向函數(shù)式編程
- 面向異步編程
- 面向泛型編程
既能做到 高開發(fā)效率 ,又能做到 高性能編程。
這里的 Span 就歸結(jié)于 高性能編程 這個范疇了。
一:Span 是什么
當(dāng)年的 C# 一個亮點(diǎn)就是屏蔽了指針,自動內(nèi)存托管,可以讓程序員更加專注于業(yè)務(wù),現(xiàn)如今策略變了,C# 要變得更加高性能,既然要做高性能那必然少不了指針,而指針又是面向 托管層 編程的程序員最怕的東西,所以就盡可能的封裝,弄一套屬于自己的托管指針玩法。
Span 即屬于托管指針玩法 的一個典型代表,如果你用 ILSpy 去看它的 struct 結(jié)構(gòu),本質(zhì)上就兩個成員,一個叫 _pointer,一個叫 _length,參考如下代碼:
public readonly ref struct Span<T>
{
internal readonly ByReference<T> _pointer;
private readonly int _length;
}
pointer 是 指定起點(diǎn), length 是 控制邊界,如果用 C 來模擬,大概就是這個樣子。
struct Span {
void* ptr;
int length;
};
畫個圖大概就是這樣子。
圖片
二:Span 的場景在哪里
有了指針,就可以對 內(nèi)存 進(jìn)行原地操作,只要能 原地操作 ,那就可以破掉 語言層面 上的諸多限制,實(shí)現(xiàn)接近 C/C++ 級的高性能,有些朋友可能要問了,語言層面有什么限制?比如最典型的 string ,大家都知道 string 是一個 writeoncopy 特性的字符串,只要你動它一下,它就會繁殖,接下來我們就拿 string 舉個例子。
1. string 中的數(shù)字求 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 進(jìn)行 Split 會導(dǎo)致生成多個小 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 指針在內(nèi)存地址上進(jìn)行移動來完成,如果看不明白,我可以用 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);
}
圖片
雖然代碼有點(diǎn)多,但邏輯還是很清楚的。
如果大家明白 Span 所封裝的底層指針玩法,我想這其實(shí)沒什么難的,本篇就說到這里吧,希望對你有幫助。