.NET 中創建支援集合初始化器的類型

  • 2020 年 2 月 10 日
  • 筆記

對象初始化器和集合初始化器只是語法糖,但是能讓你的程式碼看起來更加清晰。至少能讓對象初始化的程式碼和其他業務執行的程式碼分開,可讀性會好一些。

本文將編寫一個類型,可以使用集合初始化器構造這個類型。不只是添加元素的集合初始化器,還有帶索引的集合初始化器。


稍微提一下對象初始化器

很普通的類型就可以支援對象初始化器,只需要對象有可以 set 的屬性或者可訪問的欄位即可。

public class Walterlv  {      public string Site { get; set; }  }

初始化時可以使用

var walterlv = new Walterlv  {      Site = "https://blog.walterlv.com",  };

基本上大家編寫的類或多或少都會支援對象初始化器,所以本文不會對此談論更多的內容。

通常的集合初始化器

當你定義一個集合的時候,你會發現你的類型已經天然支援集合初始化器了。比如你定義了下面這個集合:

public class WalterlvCollection : ICollection<Walterlv>  {      // 省略集合定義的程式碼。  }

那麼此集合初始化的程式碼就可以寫成下面這樣:

var collection = new WalterlvCollection  {      new Walterlv(),      new Walterlv(),  }

實際上你會發現實現一個 ICollection 是一件非常繁瑣的事情。

▲ 實現一個 ICollection 需要實現的方法

最簡單的集合初始化器

只是做一個集合初始化器的話並不需要寫上面那麼多的程式碼。

實際上,你只需要兩個步驟:

  1. 實現 IEnumerable 介面或任何子介面
  2. 有一個 Add 方法

就像這樣:

public class WalterlvCollection : IEnumerable  {      private readonly List<Walterlv> _list = new List<Walterlv>();      public IEnumerator GetEnumerator()=>_list.GetEnumerator();      public void Add(string site) => _list.Add(new Walterlv { Site = site });  }

於是你就可以像一個一般的集合那樣去使用集合初始化器了:

var collection = new WalterlvCollection  {      "https://blog.walterlv.com/",      "https://walterlv.blog.csdn.net/",  };

多個參數的集合初始化器

剛剛我們的例子中 Add 方法只有一個參數,實際上也可以是多個參數。

public class WalterlvCollection : IEnumerable  {      private readonly List<Walterlv> _list = new List<Walterlv>();      public IEnumerator GetEnumerator()=>_list.GetEnumerator();      public void Add(string site, bool includeProtocol) => _list.Add(new Walterlv { Site = site });  }

現在初始化的方法就有點像字典了:

var collection = new WalterlvCollection  {      { "https://blog.walterlv.com/", true },      { "https://walterlv.blog.csdn.net/", true },  };

當然你也可以寫更多參數,看起來更加喪心病狂。

public class WalterlvCollection : IEnumerable  {      private readonly List<Walterlv> _list = new List<Walterlv>();      public IEnumerator GetEnumerator()=>_list.GetEnumerator();      public void Add(string site, bool includeProtocol, string author)          => _list.Add(new Walterlv { Site = site });  }
var collection = new WalterlvCollection  {      { "https://blog.walterlv.com/", true, "walterlv" },      { "https://walterlv.blog.csdn.net/", true, "walterlv" },  };

帶索引集合初始化器

如果你期望的初始化方法是索引,實際上也不需要 Add 方法。只需要增加一個索引的定義即可:

public class WalterlvCollection : IEnumerable  {      private readonly List<Walterlv> _list = new List<Walterlv>();      public IEnumerator GetEnumerator()=>_list.GetEnumerator();      public string this[string site]      {          get => _list.Find(x => x.Site == site).Site;          // 請忽略這裡的 Bug,這只是一個語法糖的示例。          set => _list.Add(new Walterlv { Site = value });      }  }

這時,可以使用索引方式的集合初始化器:

var collection = new WalterlvCollection  {      ["呂毅"] = "https://blog.walterlv.com/",      ["林德熙"] = "https://blog.lindexi.com/"  };

這是一個可以發揮創造力的語法糖

利用單個和多個參數的集合初始化器,以及帶索引的集合初始化器,我們甚至可以用集合初始化器去構造一些看起來不像集合的類型。這又是一波語法糖!

當然有一點值得注意,使用集合初始化器初始化的時候,Addthis[] 的初始化是不能同時使用的。

參考資料

事實上微軟的官方文檔中並沒有對集合初始化器的最簡實現有多少描述,所以以下的參考實際上並沒有用。

本文會經常更新,請閱讀原文: https://blog.walterlv.com/post/create-class-that-supports-collection-initializer.html ,以避免陳舊錯誤知識的誤導,同時有更好的閱讀體驗。

本作品採用 知識共享署名-非商業性使用-相同方式共享 4.0 國際許可協議 進行許可。歡迎轉載、使用、重新發布,但務必保留文章署名 呂毅 (包含鏈接: https://blog.walterlv.com ),不得用於商業目的,基於本文修改後的作品務必以相同的許可發布。如有任何疑問,請 與我聯繫 ([email protected])