7

.NET 小技巧 - 將 List/Dictionary 設為唯讀防止誤用

 7 months ago
source link: https://blog.darkthread.net/blog/ireadonlylist-and-ireadonlydictionary/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

將 List/Dictionary 設為唯讀防止誤用-黑暗執行緒

今天踩到的低級錯誤,用以下範例重現。

假設有物件 Foo,建構時傳入字串,透過 List<string> ListDictionary<string, int> Stats 兩個唯讀屬性傳回包含字元清單及出現次數統計:

public class Foo {
    public List<string> List => 
        rawData.ToCharArray().GroupBy(c => c)
            .OrderBy(g => g.Key)
            .Select(g => g.Key.ToString())
            .ToList();
    public Dictionary<string, int> Stats =>
        rawData.ToCharArray().GroupBy(c => c)
            .OrderBy(g => g.Key)
            .ToDictionary(g => g.Key.ToString(), g => g.Count());
    string rawData ;
    public Foo(string data)
    {
        rawData = data;
    }
}

每次讀取重新統計有損效能,但原始情境很單純又,只有一處程式用到,兩個屬性也只會讀一次,簡單寫寫就好。

不過呢,你永遠料想不到別人會怎麼用你的元件(這裡的別人包含未來的自己)。在其他模組借用了這顆元件,看到元件傳回 List<string> 的是 ``Dictionary<string, int>`,依直覺加工後想用在別的地方:

var foo = new Foo("AADBDACABC");
foo.List.Add("E");
foo.Stats.Add("E", 2);
Console.WriteLine(string.Join(", ", 
    foo.Stats.Select(o=> $"{o.Key}:{o.Value}")));
Console.WriteLine(string.Join(", ", foo.List));

Fig1_638427368511716539.png

結局是:明明用 Add() 做了修改,實際輸出卻什麼都沒變,追了一會兒才猛然想起問題出「List 跟 Stats 是現點現做的」。

先不探究這類別介面設計得有問題,把焦點放在「若規格就是要 List 或 Dictionary,有沒有辦法將其設成唯讀防止呼叫端誤用」?

心血來潮,這回不 Google 求解,改問神奇海螺 Github Copilot 看能否得到答案?

使用 Copilot Chat[1],用 how to prevent user from altering the properties or elements of readonly properties (ex: Stats and List)? 提問[2],由 Copilot 回答我學到 IReadOnlyListIReadOnlyDictionary,而 Dictionary<T,T>List<T> 都支援 AsReadOnly() 傳回 ReadOnlyDictionary<TKey,TValue>ReadOnlyCollection<T>

Fig2_638427368518040232.png

另一種更快的做法是,選取 List 與 Stats 屬性的程式片段[1],按 Ctrl - I 叫出提示輸入列,請 Copilot 將二者換成唯讀版[2],Copilot 會直接改寫,按下 Accept 即可套用,而套用前已可預覽修改後上方的兩個 Add() 出現紅蚯蚓[3]無法編譯:

Fig3_638427368520286698.png

注意:IReadOnlyList、IReadOnlyDictionary 只防止對清單跟雜湊表進行增刪或置換,無法防止清單或雜湊元素被修改,如下例:

Fig4_638427368522510001.png


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK