2016年6月4日 星期六

自動化設備PC程式參數檔案架構介紹與改進(二)

瞭解INI參數架構的問題靠北後,本篇文章將以序列化為主題,在這之前重述一下具體的需求:
難道不能直接改參數類別(Class),就對應到參數檔(xml)嗎?不想逐步修改loadIniFile()、saveIniFile()內容』是的,我們可以靠序列化(Serialization)達成,參考MSDN文章得知:
序列化是將物件(Object)轉換為位元組資料流(Stream of bytes)的一種處理程序,以便將其儲存或傳輸至記憶體、資料庫或檔案。主要目的是要儲存物件的狀態,以便能在需要時重新建立該物件。此回復程序稱為還原序列化 (Deserialization)。

上述的”物件”我們直接當成參數類別的實體(Instance),是以整個物件做序列化,不需要針對物件中的個別參數寫檔案,或是讀檔案後一一寫到參數。對應我們的目標,流程可以簡述如下:

  • 序列化 : 物件 -> 位元資料流 -> 存檔
  • 反序列化 : 讀檔 -> 位元資料流 -> 物件
除了MSDN相關文章,隨便問一下谷哥”序列化”,可以得到一海票的相關文章,身為一個優秀的碼猴,乖乖地吃香蕉K完這些文章並測試後,驚覺事情沒有這麼單純,還是有以下事項要注意:
  • 存檔文件的可讀性:
  • 序列化方式不只有一種,可以選用XML序列化,讓文檔具有可讀性。
  • 程式寫法解耦:
  • 單說解耦有點抽象,但其實就是要把功能寫成通用、獨立的函式,目標是用一個函式把物件序列化為文件,和一個函式反序列化文件為物件。
  • 類別和屬性的標籤簡化:
  • 要序列化的類別和屬性通常都要標示[Serializable()]之類的特性,但都已經建立專屬的參數類別了,本來就要把整個類別序列化,標示特性的動作還是有點瑣碎。
反覆測試後決定採用DataContractSerializer而不是XmlSerializer,有興趣可以看看這兩種XML序列化的比較,以及DataContractSerializer不標示特性的影響,最後用泛型讓函式可以支援多種物件,並且將檔案路徑參數化,參考以下程式碼:
//將物件序列化為檔案
public static void WriteToXml<T>(string fullFileName , T instance)
{
    FileStream writer = null;
    string directory = Path.GetDirectoryName(fullFileName);
    if (!Directory.Exists(directory))
    {
        Directory.CreateDirectory(directory);
    }
    try
    {
        writer = new FileStream(fullFileName , FileMode.Create);
        DataContractSerializer ser = new DataContractSerializer(instance.GetType());
        ser.WriteObject(writer , instance);
        writer.Close();
    }
    catch (Exception ex)
    {
        if (writer != null)
        {
            writer.Close();
        }
        throw new Exception(ex.Message);
    }
}

//反序列化檔案為物件
public static T ReadFromXml<T>(string fullFileName)
{
    FileStream fs = null;
    try
    {
        fs = new FileStream(fullFileName , FileMode.Open);
        DataContractSerializer ser = new DataContractSerializer(typeof(T));
        T deserializedData = (T)ser.ReadObject(fs);
        fs.Close();
        return deserializedData;
    }
    catch (Exception ex)
    {
        if (fs != null)
        {
            fs.Close();
        }
        throw new Exception(ex.Message);
    }
}

範例下載

2016/07/04 補充: 參數類別一定要是『public』且有『預設建構式』

沒有留言: