這篇來談兩個新功能,ref readonly parameters 與 alias any type
ref readonly parameters
在 C# 7.2 版對於方法參數的修飾詞多出一個 in 修飾詞,這個修飾詞表明這是一個 by reference 傳遞的參數,並且在方法內部無法重新指派新的值給這個參數。
C#12 居然又出現了一個 ref readonly 修飾詞,作用也差不多,所以表明這個修飾詞一定有某些作用是原來的 in 修飾詞無法做到的,來看看怎麼回事。
首先,這個修飾詞用法很簡單,如下:
public static int Do(ref readonly int x)
{
return x + 1;
}
在呼叫端可以使用 ref 或 in 修飾詞,以下兩種呼叫皆合法:
Do(ref x);
Do(in x);
這就是和 in 修飾詞在作用上的不同,如果一個方法的參數使用了 in 修飾詞,那呼叫端也得使用 in 修飾詞;但方法參數若使用 ref readonly 修飾詞,呼叫端可以使用 ref 或 in。
就個人觀點來看,這個微妙的差別在於「方法修改後保持呼叫者的使用介面毋須更動」。那我們在公堂之上假設一下,假設我們有一個自訂的函式庫撰寫在 C# 6,這個方法參數被設計為 pass by reference,而且希望方法內部不能更動此參數值,我們可能怎麼做?
public static int Do(ref int x, int y)
{
// 請勿在此更動 x 的值
return x + y;
}
使用 ref 是必要的,但如何防止其他開發者在方法內容中重新指派 x ?大致上就是加上一行註解當警語。
到了 C# 7.2 ,我們可能會想要把它改成 in,於是變成這樣:
public static int Do(in int x, int y)
{
return x + y;
}
已經不用加上警語了,那個 in 就代表了一切,而且編譯器不允許你在方法內部重新指派 x。完美嗎?如果這個方法沒有人用過,這件事就完美了。但如果這個函式屬於某個獨立的 DLL,而這 DLL 已經有一大堆人用,再加上有一堆人呼叫了這個方法;當你很開心宣布函式庫已經更新到最新版本,請大夥兒踴躍升級,那就是你電話接到手軟 (而且大部分是客訴) 的日子,因為每個人都會需要為了這個升級把所有呼叫端的 ref 改成 in。
ref readonly 就是在解決這個問題,若我們把 ref 替換成 ref readonly,呼叫端保持 ref 修飾詞還是可以的,這樣就沒有人會覺得這個修改會造成困擾了。
alias any type
其實早期就有 alias type 的功能,比方說:
using Scores = System.Collections.Generic.List<int>;
using MyPoint = System.ValueTuple<int, int>;
namespace ConsoleApp10
{
class Program
{
static void Main(string[] args)
{
Scores s = new Scores { 1, 2, 3, 4, 5 };
foreach(var item in s)
{
Console.WriteLine(item);
}
MyPoint p = (5, 10);
Console.WriteLine(p);
Console.ReadLine();
}
}
}
但有個小小的缺點,非命名類型不可用,像是 ValueTuple 語法糖或是陣列,因此在過去無法這麼寫:
using MyPoint = (int x, int y);
using Numbers = int[];
現在 C#12 這麼寫就沒問題了,陣列的部分順手用上 collection expressions。
using MyPoint = (int x, int y);
using Numbers = int[];
namespace AliasAnyTypeSample001
{
internal class Program
{
static void Main(string[] args)
{
MyPoint p = new MyPoint(10, 90);
Numbers numbers = [1,2,3,4,5,6,7,8,9,10];
}
}
}
alias type 的功能可以讓型別具有一個有意義的別名,算是個不錯的功能。