C# - 將弱型別集合體轉變為定義清楚的物件內容

分享如何將弱型別內容集合體HashTable轉變為高維護性的物件內容(DTO)。

前言

由於總總原因,職業生涯中難免會遇到一些該死的事情,其中莫過於資料傳輸時,取得的是一個完全不清楚內容欄位屬性的弱型別集合體(像是一個DataTable,HashTable......),開發與維護時必須大眼盯小眼,左邊看文件,右邊看程式碼,眼睛脫窗還不一定可以修改成功。因此將介紹如何轉變為可閱讀性相對非常高的DTO內容的處理方法。

 

案例分享

先前接手過一個專案,前人的資料處理機制基本上就是把資料庫的查詢結果放進一個HashTable內,基本上長得像是這樣。

var lisData = new List<Hashtable>() {
    new Hashtable{ {"Prop1" , 15},{"Prop2" ,DateTime.Now}  , { "Prop3" , "John" } },
    new Hashtable{ {"Prop1" , "24"},{"Prop2" ,"2018-04-18"}  , { "Prop3" , 18D} },
};

這不僅僅開發時需要檢查有無欄位名稱,後續維護與擴充時更是不知道該如何下手,簡單來說就是非常的...糟糕。

不過感謝現在已經2018年了,因此可以透過微軟釋出的映射(Reflection)以及泛型(Ceneric)來處理這個問題。因此可以做一個類似以下的程式碼來處理這種東西。

T ConvertDTO<T>(Hashtable data) where T : new()
{
    T result = new T();
    var aryPropInfo = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.SetProperty);
    foreach (var prop in aryPropInfo)
    {
        if (false == prop.CanWrite)
        {
            continue;
        }
        if (data.ContainsKey(prop.Name))
        {
            var dataType = data[prop.Name].GetType();
            var propType = prop.PropertyType;

            if (dataType.Equals(propType))
            {
                //類別相符,直接設定
                prop.SetValue(result, data[prop.Name], null);
            }
            else if (propType == typeof(string))
            {
                //當內容物為字串時,嘗試轉變為字串
                var propValue = data[prop.Name]?.ToString();
                prop.SetValue(result, propValue, null);
            }
            else if (dataType == typeof(string))
            {
                //當弱別的內容為字串時,嘗試進行轉換
                //嘗試取得轉換器
                var converter = TypeDescriptor.GetConverter(propType);
                if (converter != null)
                {
                    var strValue = data[prop.Name] as string;
                    var propValue = converter.ConvertFromString(strValue);
                    if (propValue != null)
                    {
                        prop.SetValue(result, propValue, null);
                    }
                }
            }
        }
    }
    return result;
}

PS:基本概念就是【我有一個物件需要處理,內容從HASHTABLE來,我把我的物件使用的欄位取出嘗試比對HashTable有無對應欄位資料,如果有就嘗試進行塞入填值】

接著,開一個簡單物件來代表DTO用來存取這個HashTable,後續開發與維護操作基本上以該物件為主。

 public class SampleDTO
 {
        public int Prop1 { get; set; }
        public DateTime? Prop2 { get; set; }
        public string Prop3 { get; set; }
 }

最後跑一下運行結果。

調整後就可以告別大眼瞪小眼的日子,往後維護時也再也不用擔心欄位不存在等等的問題了,這代表著加班的機會又下降了,可喜可賀。

 

後語

弱型別無所不在,HashTable只是一個例子,以往很常見的還有DataTable(就算給你AsEnumerable,還是要MAPPING半天與檢查欄位。這些弱型別與欄位定義不清的東西嚴重拖累工程師的開發效率與熱情,因此希望這篇文章能幫忙到有需要的人...

PS:儘管現在是2018年,有些地方還是不見天日,無法自由安裝第三方套件或者是無法任意重構的....