C# 7.0 的 ValueTuple

  • 5355
  • 0
  • 2017-08-03

Visual Studio 2017 將在下個月初發行第一個對外正式版本,相關的工具也會一併釋出,其中包含 C# 7.0.在 C# 7.0 中有些新的簡便語法讓開發者可以打字少一點.在這些新功能中,Tuple 多了一個新兄弟,而且是一個 value type,所以叫做 ValueTuple.這篇文章將簡單地介紹 ValueTuple.

舊的 Tuple 是 reference type,當你要使用它時,便需要 new 一個 Tuple 物件,然後把值放進去,下面的例子就是一個傳統的 Tuple 例子.

        static void Main(string[] args)
        {
            int[] numbers = { 1, 2 };
            Tuple<double, double> result = CalSumAndAvg(numbers);
            Console.WriteLine($"sum = {result.Item1}");
            Console.WriteLine($"avg = {result.Item2}");
            Console.ReadKey();
        }

        static Tuple<double, double> CalSumAndAvg(int[] numbers)
        {
            double sum = 0;
            double avg;
            for (int i = 0; i < numbers.Length; i++)
            {
                sum += numbers[i];
            }
            avg =  sum / numbers.Length;
            return new Tuple<double, double>(sum, avg);
        }

我個人並不喜歡用 Tuple,因為存取內容時得用 Item1, Item2 這種名稱,用起來有點蠢,看起來也很好笑,因為記不起來 Item1, Item2 是什麼東西.假設你用了它,我相信過一段時間後,你也會忘了你當初寫的 Item1 , Item2 是什麼.通常都需要把前後的程式碼看完才知道 Item1, Item2 代表什麼東西.在新的 Tuple 中,這點可以克服了.為了要使用新的 Tuple,必須從 NuGet 上下載 System.ValueTuple.

首先, 可以把 Tuple<double, double> 直接改成 (double, double),而且新版的 Tuple 是 value type,所以不需要建立 Tuple 物件.

因為,上述的 CalSumAndAvg 可以改成如下:

        static (double, double) CalSumAndAvg(int[] numbers)
        {
            double sum = 0;
            double avg;
            for (int i = 0; i < numbers.Length; i++)
            {
                sum += numbers[i];
            }
            avg =  sum / numbers.Length;
            return (sum, avg);
        }

在原本的 Main 也能改成如下:

        static void Main(string[] args)
        {
            int[] numbers = { 1, 2 };
            (double,double) result = CalSumAndAvg(numbers);
            Console.WriteLine($"sum = {result.Item1}");
            Console.WriteLine($"avg = {result.Item2}");
            Console.ReadKey();
        }

接下來,為了要擺脫前面所說的 Item1, Item2,在使用 ValueTuple 時,你可以直接設定每個 item 的名稱,然後在操作該 tuple 時,就可以直接用設定好的名字做為 property,如下所示:

        static void Main(string[] args)
        {
            int[] numbers = { 1, 2 };
            (double Sum, double Avg) result = CalSumAndAvg(numbers);
            Console.WriteLine($"sum = {result.Sum}");
            Console.WriteLine($"avg = {result.Avg}");
            Console.ReadKey();
        }

從上面看起來,用 Sum, Avg 來做為 property name 比起 Item1, Item2 好多了.

以此類推的想法,你也可以設定一個初始值給 ValueTuple,同時設定每個 item 的名字,然後直接用名字去操作 property,如下所示:

        static (double, double) CalSumAndAvg(int[] numbers)
        {
            var data = (s: 0.0, a: 0.0);

            for (int i = 0; i < numbers.Length; i++)
            {
                data.s += numbers[i];
            }
            data.a = data.s / numbers.Length;
            return data;
        }

如果你喜歡用 Tuple 的話,相信這個 ValueTuple 會是你較喜歡的版本.同時,由於 ValueTuple 是 value type,所以它使用的記憶體空間不會在 managed heap,而是在 program stack 裡,因此使用 ValueTuple 不會有 garbage collection 在它身上發生.

ValueTuple 的原始程式碼在 https://github.com/dotnet/corefx/blob/master/src/System.ValueTuple/src/System/ValueTuple/ValueTuple.cs

Hope it helps,