C# 8.0 搶先看 -- Ranges and Indicies (2) Range

前篇介紹完 Index,該是讓 Range  登場的時候。

本篇文章使用環境
開發環境 Visual Studio 2019 Preview 1 (16.0.0 Preview 1)
框架       .NET Core 3.0.0-preview-27122-01
編譯器    C# 8.0 beta

Range structure 和 Index structure (參考前一篇) 有密不可分的關係,從 Range 的公開成員看起:

namespace System
{
    public struct Range : IEquatable<Range>
    {
        public Index End { get; }
        public Index Start { get; }

        public static Range All();
        public static Range Create(Index start, Index end);
        public static Range FromStart(Index start);
        public static Range ToEnd(Index end);
        public override bool Equals(object value);
        public bool Equals(Range other);
        public override int GetHashCode();
        public override string ToString();
    }
}

Range 有兩個重要的公開成員屬性,分別是 Start 和 End,從上面的程式碼可以看出來它們的型別都是 Index,這也就是定義了 Range 的範圍值,依據Bill目前的測試來看,Range 的定義為 Start ≦  X <End

建立 Range 的最基本方式是利用三個靜態的方法:

(1) Create:給予 Start 與 End 的值設定範圍
(2) FromStart:只給 Start 值,表示範圍從 Start 開始直到末端(index ^0)
(3) ToEnd:只給 End 值,表示範圍從開頭(index 0)到 End

 例如:

    class Program
    {
        static void Main(string[] args)
        {
            Range r1 = Range.Create(1, ^1);
            Range r2 = Range.FromStart(1);
            Range r3 = Range.ToEnd(3);
            Display(r1);
            Display(r2);
            Display(r3);
            Console.ReadLine();
        }

        static void Display(Range range)
        {
            Console.WriteLine(range);
        }
    }

結果會顯示為:

1..^1
1..^0
0..3

C# 8.0 為 Range 也新增了一個運算子 "..", 這兩個點點意思就差不多是"從哪到哪 (form to)"的意思。還是那句老話,懶還要更懶,這個運算子可以快速地建立 Range:

    class Program
    {
        static void Main(string[] args)
        {
            var array = new string[] { "A", "B", "C", "D", "E", "F", "G" };
            var ranges = new List<Range>
            {
                {..},
                {0..^0},
                {1..3 },
                {1..^1},
                {1.. },
                {..^2},
            };

            foreach (var range in ranges)
            {
                foreach (var s in array[range])
                {
                    Console.Write(s);
                }
                Console.WriteLine(); 
            }
            Console.ReadLine(); 
        }
    }
當使用 .. 的時候,代表的就是 0..^0,也就是 Range.All() 的回傳。
1..3   代表的是介於 1~3  之間,但不包含 3 及以後,也就是回傳 "BC"。
1..^1 代表的是介於 1~ 倒數第1個之間,但不包含倒數第1個及以後,所以回傳 "BCDEF"
1..     代表的是從1 開始直到最後,所以回傳 "BCDEFG"
..^2   代表從起始到倒數第2個,但不包含倒數第2個及以後,所以回傳 "ABCDE"

老實說,這以前也不是沒有解法,linq 中有個 Skip - Take 也可以做到類似的效果,例如:

    class Program
    {
        static void Main(string[] args)
        {
            var array = new string[] { "A", "B", "C", "D", "E", "F", "G" };
            var ranges = new List<ValueTuple<int, int>>
            {
               (0, array.Length),
               (0, array.Length),
               (1, 2),
               (1, array.Length -2),
               (1, array.Length -1),
               (0, array.Length -2)

            };

            foreach (var range in ranges)
            {
                foreach (var s in array.Skip(range.Item1).Take(range.Item2))
                {
                    Console.Write(s);
                }
                Console.WriteLine();
            }
            Console.ReadLine();
        }
    }

這兩種用法其實相輔相成,如果資訊上的條件比較適合的是『從A索引到B索引的範圍』,那 Range 會比較直覺;如果條件比較適用於『跳過幾筆取得幾筆』的情境,那 Skip - Take 會比較順手。

Ranges and Indices 的 Sample 可以參考我的 github:RangesAndIndicesSamples