C# Span 入門


本文簡單告訴大家如何使用 Span 新的功能

需要知道 Span 是 7.2 才有的功能,如果在正式項目使用,建議安裝 Nuget 的方式

在對內存指定的一段的處理,原來的 C# 是比較弱的,因為沒有了 C++ 的指針,特別是對於字符串的分割,需要創建多幾個字符串。

垃圾微軟為了提高 C# 的性能,於是提供了新的類型 Span,這個類可以拿出任意數組的一段,作為一個新的 Span 列表。這樣的底層就是指針,而且指針是有判斷是否超過范圍比 C++ 安全。

首先安裝 Nuget System.Memory 庫,要求 dotnet framework 4.5 以上,在 UWP 16299 以上,在 dotnet core 2.0 以上

在這里插入圖片描述

先來寫一個簡單的程序,創建一個數組,然后使用 Span 指定數組的某一段

            var array = new byte[10];
            Span<byte> bytes = array; 
            bytes = bytes.Slice(start: 2, length: 5);

            bytes[0] = 5;
            Console.WriteLine(array[2]);
            Console.WriteLine(bytes[0]);

可以看到對 bytes[0] 的修改就是對 array[2] 的修改,這樣可以做到數組重新計算。

也就是對某個計算,需要加上數組的移動,如二維數組的圖片計算,例如行是 w 列是 h ,那么計算第 n 行的元素,在以前的時候,就需要在每個的前面加上 w*n,現在可以使用 spanList.Slice(start:w*n, Length:w) 這樣重新拿到的數組就是一行,不需要在每個計算都需要添加很多代碼

            var array = new byte[10];
            Span<byte> bytes = array; 
            bytes = bytes.Slice(start: 2, length: 5);

            bytes[0] = 5;
            Console.WriteLine(array[2]);
            Console.WriteLine(bytes[0]);

            try
            {
                bytes[5] = 2;
            }
            catch (IndexOutOfRangeException e)
            {
                Console.WriteLine(e);
            }

有了這個類就不需要擔心寫出超過范圍代碼

stackalloc

如果要和 stackalloc 需要打開不安全代碼

在這里插入圖片描述

然后點擊生成高級,選擇 C# 7.2 以上

現在可以寫出這樣的代碼

        private static unsafe void DroosorHotir()
        {
            Span<byte> bytes = stackalloc byte[2];
            bytes[0] = 2;
            bytes[1] = 3;

            Console.WriteLine(bytes[0]);
            Console.WriteLine(bytes[1]);
        }

調用這個函數可以輸出 2 和 3 ,使用 stackalloc 可以比申請數組使用更少的資源。因為默認申請的數組都在堆中,不使用需要 gc 才可以回收。但是 stackalloc 可以在變量所在函數結束之后直接就回收,不需要移動內存。

但是 stackalloc 容易出現堆棧溢出,請執行下面的代碼,堆棧溢出是 catch 也無法讓他不讓程序直接退出

            Span<double> bytes = stackalloc double[200000];

即使使用 catch ,軟件也會直接退出

            try
            {
                Span<double> bytes = stackalloc double[200000];
            }
            catch (Exception)
            {
               // 接不住
            }

AllocHGlobal

除了使用 stackalloc 之外,還可以使用 Marshal.AllocHGlobal 申請一段內存

            IntPtr ptr = Marshal.AllocHGlobal(2);
            try
            {
                var bytes = new Span<byte>((byte*) ptr, 2) {[0] = 42};

                Console.WriteLine(bytes[0]);
                Console.WriteLine(Marshal.ReadByte(ptr));
            }
            finally
            {
                Marshal.FreeHGlobal(ptr);
            }

需要注意,申請的內存都需要自己釋放

而且需要注意,不要使用比自己申請的內存大的數組

            IntPtr ptr = Marshal.AllocHGlobal(2);
            var bytes = new Span<byte>((byte*) ptr, 1000) {[0] = 42};

上面代碼申請了內存為 2 但是下一句使用了1000長度


                for (int i = 0; i < 1000; i++)
                {
                    bytes[i] = 25;
                    Console.WriteLine(bytes[i]);
                }

這時雖然很多次都可以賦值成功,但是運行到某個時候,軟件就直接退出。

參考:

C# - All About Span: Exploring a New .NET Mainstay

我搭建了自己的博客 https://lindexi.gitee.io/ 歡迎大家訪問,里面有很多新的博客。只有在我看到博客寫成熟之后才會放在csdn或博客園,但是一旦發布了就不再更新

如果在博客看到有任何不懂的,歡迎交流,我搭建了 dotnet 職業技術學院 歡迎大家加入

知識共享許可協議
本作品采用知識共享署名-非商業性使用-相同方式共享 4.0 國際許可協議進行許可。歡迎轉載、使用、重新發布,但務必保留文章署名林德熙(包含鏈接:http://blog.csdn.net/lindexi_gd ),不得用於商業目的,基於本文修改后的作品務必以相同的許可發布。如有任何疑問,請與我聯系


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM