C# - App.config自訂section程式碼架構Part III(使用泛型)

第一篇介紹過 App.config 的基本觀念,與第二篇說明了 App.config 的進階用法後,再來本篇沒有要說新的觀念,只是在前面兩篇中,我們看到一個愈複雜的 App.config,其後面伴隨著要寫許多程式碼。其實這些程式碼是可以簡化的,這邊限量就要來說明如何簡化使用 App.config 的程式碼。

我們簡化的觀念很簡單,就是使用泛型來動態決定處理的類別。

我們從第二篇的陣列範例來看,仔細的去發現程式碼的規律,可以發現到,繼承 ConfigurationElementCollection 的 Collection 類別只需要覆寫 2 個 Method 與實作 Index 就可以合法使用了,而且每個類別極度相似,只有在 Element 這邊是不同的,因此限量覺得在 Collection 的部分可以簡化。但是注意,GetElementKey 需要去指定 Element 的某個可當作 Key 的 Property,泛型的篩選條件就不能直接用最基本的 T 繼承 ConfigurationElement 來判斷,所以限量就實作一個抽象 Element 類別來解決這個問題。

ConfigEleBase.cs
public class ConfigEleBase : ConfigurationElement
{
    [ConfigurationProperty("name", IsRequired = true)]
    public string Name
    {
        get
        {
            return this["name"] as string;
        }
        set
        {
            this["name"] = value;
        }
    }
}
ConfigEleBase 是繼承 ConfigurationElement 的抽象類別,裏頭只有一個 Name 的 Property,因為是要讓 Collection 的 GetElementKey 當作 Key 的識別,所以這個 Name 就把它設置為 IsRequired = true。接著其他 Element 類別只要改繼承 ConfigEleBase 就可以了。

再回頭看看 Collection 的程式如何簡化:

GenericConfigEleCollection.cs
public class GenericConfigEleCollection<T> : ConfigurationElementCollection where T : ConfigEleBase, new()
{
    protected override ConfigurationElement CreateNewElement()
    {
        return new T();
    }

    protected override object GetElementKey(ConfigurationElement element)
    {
        return (element as T).Name;
    }

    public T this[int index]
    {
        get
        {
            return this.BaseGet(index) as T;
        }
    }
}

GenericConfigEleCollection 只是把 Element 的部分改成 T,另外要加上 T 的篩選條件,T 繼承 ConfigEleBase。另外因為在這邊會產生 T 的實體,所以篩選條件後面要加 new()

接著就來看看原本的程式要怎麼改:

只要將 CacheEle.cs 改成繼承 ConfigEleBase,並且將 Name 的 Property 刪掉。再來到 SettingSection.cs 中,把 CacheEleCollection 取代成 GenericConfigEleCollection<CacheEle> 就好了。改完了執行就可以發現結果是相同的。

CacheEle.cs
public class CacheEle : ConfigEleBase
{
    [ConfigurationProperty("path", IsRequired = true)]
    public string Path
    {
        get
        {
            return this["path"] as string;
        }
        set
        {
            this["path"] = value;
        }
    }

    [ConfigurationProperty("Global")]
    public CacheGlobalEle Global
    {
        get
        {
            return this["Global"] as CacheGlobalEle;
        }
    }

    [ConfigurationProperty("Session")]
    public CacheSessionEle Session
    {
        get
        {
            return this["Session"] as CacheSessionEle;
        }
    }
}

SettingSection.cs
public class SettingSection : ConfigurationSection
{
    [ConfigurationProperty("name", IsRequired = true)]
    public string Name
    {
        get
        {
            return this["name"] as string;
        }
        set
        {
            this["name"] = value;
        }
    }

    [ConfigurationProperty("type", IsRequired = false)]
    public string Type
    {
        get
        {
            return this["type"] as string;
        }
        set
        {
            this["type"] = value;
        }
    }

    [ConfigurationProperty("Caches")]
    [ConfigurationCollection(typeof(CacheEle), AddItemName = "Cache")]
    public GenericConfigEleCollection<CacheEle> Caches
    {
        get
        {
            return this["Caches"] as GenericConfigEleCollection<CacheEle>;
        }
    }
}

看到這樣了如果還是嫌程式碼太多的話,好吧,其實我們從 Element 的類別看也可以看到很多規律,像是 Property 的 get set 實作方式, 加入的ConfigurationProperty, 繼承 ConfigurationElement...等,這些都是可以改善的。限量提供一個想法,就是可以利用 .Net 的動態建立型別的方式,我只要定義好一個 Element 類別的 Template,然後只要傳入要當作 Config 的資料結構,接著使用反射去取得資料結構的屬性,再來把這些屬性填入 Template 再 動態編譯就 OK 了。因為實作實在是太複雜了,所以就靠自己去發想吧,有機會限量再來分享。



站內相關文章:
C# - App.config自訂section程式碼架構Part I(基本用法)
C# - App.config自訂section程式碼架構Part II (巢狀, 陣列)





留言