1. 程式人生 > >.NET 中建立支援集合初始化器的型別

.NET 中建立支援集合初始化器的型別

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

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

稍微提一下物件初始化器

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

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

初始化時可以使用

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

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

通常的集合初始化器

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

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

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

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

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

實現一個 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://walterlv.com/",
    "https://blog.csdn.net/wpwalter",
};

多個引數的集合初始化器

剛剛我們的例子中 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://walterlv.com/", true },
    { "https://blog.csdn.net/wpwalter", 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://walterlv.com/", true, "walterlv" },
    { "https://blog.csdn.net/wpwalter", 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://walterlv.com/",
    ["林德熙"] = "https://lindexi.gitee.io/"
};

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

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

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

參考資料

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