2016年3月27日 星期日

Implementation Patterns - Kent Beck (Chapter 3 價值觀,原則, 模式)

第 3 章 - 程式設計理論

價值觀,原則,模式 這三種元素組成了一種穩定的開發方式:
  • 價值觀 - 提供了動機 (why)
  • 原則 - 實際行動 (what)
  • 模式 - 如何做 (how)


價值觀:
  1. 溝通 - 把程式寫成一個故事,讀起來像一本書一樣。
  2. 簡單 - 去掉多餘的複雜性,讓讀者看得懂。
  3. 靈活 - 只有真正發生變化的時候才需要靈活性 (不用想像明天或許會用得上的靈活)。

      重要性為 溝通>簡單>靈活

原則:
  1. 確保局部化影響 - 把組織程式碼的影響範圍縮到最小,程式碼就會有極佳的溝通效果。
  2. 消除重複 - 把程式拆成許多更小的部分:小方法, 小物件, 小 package 有助於發現並消除重複。
  3. 綑綁邏輯與資料 - 把邏輯和資料放在同一個方法,同一個物件,同一個 package,讓影響發生在局部。
  4. 建立對稱性 - 邏輯概念上的對稱。例如: Add () 與 Delete ()放在同一個物件。
  5. 使用宣告式表達 - 使用 Annotation (或者 attribute) 表達程式意圖。
  6. 確保相同變化率 - 物件中所有property 的 life time 應該一樣。較短 life time 的變數應該屬於某個方法。

模式:
  •       請參閱接下來的章節...


2016年3月25日 星期五

Get and change CD ROM drive letter (修改光碟機代號)

CD ROM 的 drive letter 記錄在 Windows Registry 裡面:
  • HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\CD Burning\DriveIndex

若要使用 C# 修改CD ROM 的 drive letter, 必須透過 WMI 取得目前的drive letter, 再透過 WinAPI 改變drive letter。

1). 透過 WMI 取得目前的drive letter -

先加入 System.Management.dll 參考,在使用SelectQuery ManagementObjectSearcher ,如下:


SelectQuery queryCDROM = new SelectQuery("SELECT * FROM Win32_cdromdrive");
ManagementObjectSearcher searcherCDROM = new ManagementObjectSearcher(queryCDROM);
       
 foreach (ManagementObject cdromLetter in searcherCDROM.Get())
{
        string letter = cdromLetter["Drive"] + @"\";
        ChangeDriveLetter(letter); // sub-function to change drive letter, please see 2). 
}


2). 透過 WinAPI 改變drive letter - 
  • 先用DllImport把下列三個win api 載入進來:
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool GetVolumeNameForVolumeMountPoint(string lpszVolumeMountPoint, [Out] StringBuilder lpszVolumeName, uint cchBufferLength);

[DllImport("kernel32.dll")]
static extern bool DeleteVolumeMountPoint(string lpszVolumeMountPoint);

[DllImport("kernel32.dll")]
static extern bool SetVolumeMountPoint(string lpszVolumeMountPoint, string lpszVolumeName);


  • 在ChangeDriveLetter() 方法中,使用上述的三個win api:
        private void ChangeDriveLetter(string driveLetter)
        {
            const int MAX_PATH = 260; 
            StringBuilder volume = new StringBuilder(MAX_PATH);
            if (!GetVolumeNameForVolumeMountPoint(driveLetter, volume, (uint) MAX_PATH))
            {
                Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
            }

            if (!DeleteVolumeMountPoint(driveLetter))
            {
                Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
            }

            if (!SetVolumeMountPoint(@"W:\", volume.ToString()))  // assume to change to W:\
            {
                Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
            }
        }

** 上面列的程式碼,是先 delete 舊有的drive letter, 在 set 新的drive letter, 使用上要很小心,避免delete 非預期的drive letter。 


Use LibZ to distribute the WPF applications or libraries as single file ( 把WPF應用程式整合成一個執行檔 )

ILMerge的使用有下列限制:
  • 若.Net 應用程式有使用reflection, native DLLs, 或是3rd party 有使用 reflection, 則 ILMerge無法將應用程式整合成一個執行檔。
  • ILMerge 不能把 WPF 應用程式與其他 .Net assemblies 整合成為單一執行檔 (因為 WPF 把 .Net assemblies 編碼成 binary resources ,造成 ILMerge 不能修改這些 assemblies )。


 LibZ 可以解決ILMerge的限制:
  • 即使.Net 應用程式有使用reflection, native DLLs, 或是3rd party 有使用 reflection, 使用 LibZ 也可以把這些原件 embed 成為單一執行檔。(參閱: 這裡)
  • 根據上述,LibZ 可將WPF 應用程式與其他 .Net 的DLL檔案 embed 成為單一執行檔。


下載 LibZ - 使用VS2015 的 NuGet Package Manager (註 1)
  • 由 Reference 的 Manage NuGet Packages...下載 LibZ.Tool (註 2),如下:












  • 或是由Package Manager Console 輸入下載指令,如下:
 PM>Install-Package Libz.Tool 






















使用 LibZ 來embed 所有的DLL檔案:

  • 透過command-line (或是Post-build event command line) 輸入以下指令 - 
libz inject-dll --assembly <MyApp.exe> --include *.dll
  • 然後,<MyApp.exe> 檔案 size 會變大,就變成一個單一執行檔。 
[註 1]
NuGet Package Manager 是由 Microsoft 開發的一組Tools,整合在 VS2015 裡面。可以針對 VS的 Project 所需要的 Package (ex: 3rd party components, Resharper, Libz, Fody...) 進行下載,安裝/反安裝,升級,建構 。

[註 2]
NuGet Package Manager 下載的檔案會放在預設的packages folder 裡面。packages folder的位置 與 solution 位置相同,如下圖所示:


若要修改下載路徑,作法如下:
1). 在solution 相同位置建立一個 nuget.config 檔案
2). 編輯 nuget.config,如下圖所示:




2016年3月17日 星期四

Cast object to its actual type (把Object 轉成它的真正型別)

要把Object 轉成它的真正型別 (ex: GoodBaseStep type),可以有下列 5 種方式:

** 如果知道它的真正型別 -

               // explicit cast: if failed, throw 'InvalidCastException'
               var goodStep = (GoodBaseStep)obj;
               goodStep.HelloFunction();

               // as operator: if failed, return null
               var goodStep = obj as GoodBaseStep;
               goodStep.HelloFunction();



** 如果不知道它的真正型別 -

  • reflection
  • implementing a well-known interface
  • dynamic

例如:

             // reflection
             obj.GetType().GetMethod("HelloFunction").Invoke(obj, null);

             // interface
             IBaseStep step = (IBaseStep)obj;
             step.HelloFunction();

             // dynamic: represents an object whose operations will be resolved at runtime
             dynamic step= obj;
             step.HelloFunction();