其实说到ref,不少同窗对它已经有所了解,ref是C# 7.0的一个语言特性,它为开发人员提供了返回本地变量引用和值引用的机制。
Span
不管是ref仍是out关键,都是一种比较难以理解和操做的语言特性,如C语言中操做指针同样,这样的高级语法老是什么带来一些反作用,可是我不认为这有什么,并且不是每个C#开发者都要对这些内部运行的机制有着深入的理解,我以为不论什么复杂的东西只是为人们提供了一个自由的选择,风险和灵活性永远是不能兼容的。github
来看几个例子来讲明引用与指针的相同性,固然下面的使用方式早在C# 7.0以前就可使用了:算法
public static void IncrementByRef(ref int x) { x++; } public unsafe static void IncrementByPointer(int* x) { (*x)++; }
上面两个函数分别是使用ref和非安全指针来完成参数+1。数组
int i = 30; IncrementByRef(ref i); // i = 31 unsafe{ IncrementByPointer(&i); } // i = 32
下面是C# 7.0提供的特性:安全
int i = 42; ref var x = ref i; x = x + 1; // i = 43
这个例子中为本地 i 变量的引用 x, 当改变x的值时i变量的值也改变了。架构
ref returns是C# 7中一个强大的特性,下面代码是最能体现其特性的,该函数提供了,返回int数组中某一项的引用:函数
public static ref int GetArrayRef(int[] items, int index) => ref items[index];
经过下标取得数组中的项目的引用,改变引用值时,数组也会随之改变。性能
System.Span
如何使用呢?在.Net Core 2.0 SDK建立的项目下引用以下NuGet包:this
<ItemGroup> <PackageReference Include="System.Memory" Version="4.4.0-preview1-25305-02" /> <PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="4.4.0-preview1-25305-02" /> </ItemGroup>
在上面咱们看到了使用ref关键字能够提供的相似指针(T*)的操做单一值对象方式。基本上在.NET体系下操做指针都不认为是一件好的事件,固然.NET为咱们提供了安全操做单值引用的ref。可是单值只是用户使用“指针”的一小部分需求;对于指针来讲,更常见的状况是操做一系列连续的内存空间中的“元素”时。
Span
Span
下面来看下Span
public struct Span<T> { ref T _reference; int _length; public ref T this[int index] { get {...} } ... } public struct ReadOnlySpan<T> { ref T _reference; int _length; public T this[int index] { get {...} } ... }
接下来我会用一个直观的例子来讲明Span
若有一个字符串string content = "content-length:123",要转换将123转换为整型,一般的作法是先Substring将与数字字符无关的字符串进行截断,转换代码以下:
string content = "content-length:123"; Stopwatch watch1 = new Stopwatch(); watch1.Start(); for (int j = 0; j < 100000; j++) { int.Parse(content.Substring(15)); } watch1.Stop(); Console.WriteLine("\tTime Elapsed:\t" + watch1.ElapsedMilliseconds.ToString("N0") + "ms");
为何使用这个例子呢,这是一个典型的substring的使用场景,每次操做string都会生成新的string对象,固然不光是Substring,在进行int.Parse时重复操做string对象,若是大量操做就会给GC形成压力。
使用Span实现这个算法:
string content = "content-length:123"; ReadOnlySpan<char> span = content.ToCharArray(); span.Slice(15).ParseToInt(); watch.Start(); for (int j = 0; j < 100000; j++) { int icb = span.Slice(15).ParseToInt(); } watch.Stop(); Console.WriteLine("\tTime Elapsed:\t" + watch.ElapsedMilliseconds.ToString("N0") + "ms");
这里将string转换为int的算法利用ReadonlySpan实现,这也是Span
转换代码以下:
public static class ReadonlySpanxtension { public static int ParseToInt(this ReadOnlySpan<char> rspan) { Int16 sign = 1; int num = 0; UInt16 index = 0; if (rspan[0].Equals('-')){ sign = -1; index = 1; } for (int idx = index; idx < rspan.Length; idx++){ char c = rspan[idx]; num = (c - '0') + num * 10; } return num * sign; } }
上述两段代码100000次调用的时间以下:
String Substring Convert: Time Elapsed: 18ms ReadOnlySpan Convert: Time Elapsed: 4ms
目前Span
GitHub:https://github.com/maxzhang1985/YOYOFx 若是觉还能够请Star下, 欢迎一块儿交流。
.NET Core 开源学习群:214741894