C# 8.0 搶先看 -- Ranges and Indicies (1) Index

C# 8.0 加入了新的方式使用 Ranges and Indices (範圍與索引),來看看吧。

.NET Core 3.0 為了此新增功能引入了兩個新的結構型別,分別是 Range structure 和 Index structure。這一篇先從 Index 開始看起。

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

Index structure 是為了索引而生的, 先來看看它有哪些公開成員:

namespace System
{
    public struct Index : IEquatable<Index>
    {
        public Index(int value, bool fromEnd);

        public bool FromEnd { get; }
        public int Value { get; }

        public bool Equals(Index other);
        public override bool Equals(object value);
        public override int GetHashCode();
        public override string ToString();

        public static implicit operator Index(int value);
    }
}

從建構式開始看起,建構式的第一個參數傳入的是個 int 表示位置,而第二個參數是個 bool。這個 bool 是 Index 的重點,此值為 false 時,表示從開頭算起;若為 true 則表示從後往前推算,白話文就稱為『倒數第幾個』。如果想要確認這個 Index 是從前面算還是從後面算,可以依據 FormEnd 屬性的值來判斷。

先來寫個簡單範例 (註 -- 截至寫這篇文章的時間為止,Index 只能使用在陣列上,可能是因為此時公布的 .NET Core 還需要修正):

    class Program
    {
        private static string[] _array;       
        static void Main(string[] args)
        {
            _array = new string[] { "A", "B", "C", "D", "E" };          
            Index index1 = new Index(1, false);
            Index index2 = new Index(1, true);

            Console.WriteLine(GetString(index1));
            Console.WriteLine(GetString(index2));

            Console.ReadLine();
        }
        static string GetString(Index index)
        {
            return _array[index];
        }        
    }

這個執行的結果是 "B""E",第一個結果比較沒有甚麼問題,和你傳一個 int 型別的 1 進來是一樣的結果 ( C# 索引是從零開始的);第二個就比較有趣了,他回傳的結果是陣列的倒數第一個元素,於是乎當 formEnd 是 true 的時候,尾數索引是從 1 開頭的,意即這個位置是 (Array.Length - 1)。

依據 C# 懶還要更懶的原則,建立 Index 可以有更快的方法,如下所示:

Index index3 = 2;
Index index4 = ^2;

C# 8.0 新增了一個運算子 "^",用在 Index 上就代表倒數的意思,例如上方的 Index index4 = ^2; 意思就是 Index index4 = new Index(2, true); ;表示倒數第二個位置。那 ^0 呢?對於 ^0 會取得該陣列的 length,如果對範例中的 GetString 方法傳入 ^0 就會擲出 IndexOutOfRangeException,如下的程式碼:

    class Program
    {
        private static string[] _array;
        static void Main(string[] args)
        {
            _array = new string[] { "A", "B", "C", "D", "E" };
            Console.WriteLine(GetString(^0)); // this will cause IndexOutOfRangeException
            Console.ReadLine();
        }
        static string GetString(Index index)
        {
            return _array[index];
        }
    }

可以看得出來,對於從前面起算的索引,這個新的 Index 型別並沒有多大的用處;它好用的地方在於要倒過來算的時候,以下程式碼展示了 GetString(_array.Length - 1); 與 GetString(^1); 會得到相同的結果:

    class Program
    {
        private static string[] _array;
        static void Main(string[] args)
        {
            _array = new string[] { "門", "安", "天", "四", "六", "念", "紀" };
            var s1 = GetString(_array.Length - 1);
            var s2 = GetString(^1);  // same as (_array.length -1)
            Console.WriteLine($"{s1} : {s2}");
            Console.ReadLine();
        }
        static string GetString(Index index)
        {
            return _array[index];
        }
    }

另一個 Index 型別的使用場景和 Range 型別有關,下一篇介紹 Range 的用法。