2017年4月1日 星期六

C# 6.0 的 string 與 $ 符號的應用

現在 Visual Studio 2015 後支援 C# 6.0
string 出現了 $ 符號的應用。
原來字串組合是這麼寫的:

public string ID { set; get; }
public string Name { set; get; }
public string FullName = string.Format("{0}-{1}", ID, Name);

現在可以這樣寫:

public string ID { set; get; }
public string Name { set; get; }
public string FullName = $"{ID}-{Name}";

兩種寫法是一樣的,但是卻大大的增加可讀性。

2017年3月1日 星期三

sealed class 禁止繼承

實做上
通常建立一些管理物件
為了統一控管
不想被多個物件操作
我們可以利用 sealed class 的關鍵字
禁止其他類別繼承 sealed class
藉此操作物件內的資料
以下是 sealed class 的用法

  1. sealed public class S3Mgr
  2. {
  3. ....
  4. }


只要繼承 S3Mgr 就會編譯錯誤







partial class 將 class 分成多個 cs 檔

假設某些專案規模很大
總有些 cs 檔案
常常有人修改
程式碼也很多
每每簽入都要處理衝突
或許重構可以根本解決這樣的問題
但是當時間有限沒有足夠的時間做重構
 partial class 或許可以解燃眉之急
利用 partial class 可以將 class 分成多個 cs 檔案

  1. // A.cs 檔
  2. partial class Program
  3. {
  4. public A _a = new A();
  5. ....
  6. }

  1. // B.cs 檔
  2. partial class Program
  3. {
  4. public B _a = new B();
  5. ....
  6. }

編譯器會將 A.cs 與 B.cs 視作是同一個檔案同一個 class
也就是說 A.cs 可以直接取到 B 類別
 B.cs 可以直接取到 A 類別
就像是在同一個 class 一樣

這樣的手法
可以幫你分類功能
依據你需要修改的內容
移到獨立的 cs 檔案內
避免多人修改同一個 cs 檔
也能方便管理類別
避免類別內過多的程式碼
有助於程式碼的閱讀



C# Composite Pattern 組合模式


組合模式最關鍵的地方是簡單對像和復合對像實現相同的接口

  1. /// <summary>
  2. /// 圖形抽像類
  3. /// </summary>
  4. public abstract class Graphics
  5. {
  6. public string Name { get; set; }
  7. public Graphics(string name)
  8. {
  9. this.Name = name;
  10. }
  11. public abstract void Draw();
  12. public abstract void Add(Graphics g);
  13. public abstract void Remove(Graphics g);
  14. }

簡單圖形

  1. /// <summary>
  2. /// 簡單圖形類——線
  3. /// </summary>
  4. public class Line : Graphics
  5. {
  6. public Line(string name)
  7. : base(name)
  8. { }
  9. // 重寫父類抽像方法
  10. public override void Draw()
  11. {
  12. Console.WriteLine("畫線:" + Name);
  13. }
  14. public override void Add(Graphics g)
  15. {
  16. throw new Exception("不能向簡單圖形Line添加其他圖形");
  17. }
  18. public override void Remove(Graphics g)
  19. {
  20. throw new Exception("不能向簡單圖形Line移除其他圖形");
  21. }
  22. }
  23. /// <summary>
  24. /// 簡單圖形類——圓
  25. /// </summary>
  26. public class Circle : Graphics
  27. {
  28. public Circle(string name)
  29. : base(name)
  30. { }
  31. // 重寫父類抽像方法
  32. public override void Draw()
  33. {
  34. Console.WriteLine("畫圓:" + Name);
  35. }
  36. public override void Add(Graphics g)
  37. {
  38. throw new Exception("不能向簡單圖形Circle添加其他圖形");
  39. }
  40. public override void Remove(Graphics g)
  41. {
  42. throw new Exception("不能向簡單圖形Circle移除其他圖形");
  43. }
  44. }

複雜圖形

  1. /// <summary>
  2. /// 複雜圖形,由一些簡單圖形組成,假設該複雜圖形由兩條線組成
  3. /// </summary>
  4. public class ComplexGraphics : Graphics
  5. {
  6. private ListGraphics complexGraphicsList = new ListGraphics>();
  7. public ComplexGraphics(string name)
  8. : base(name)
  9. { }
  10. /// <summary>
  11. /// 複雜圖形的畫法
  12. /// </summary>
  13. public override void Draw()
  14. {
  15. foreach (Graphics g in complexGraphicsList)
  16. {
  17. g.Draw();
  18. }
  19. }
  20. public override void Add(Graphics g)
  21. {
  22. complexGraphicsList.Add(g);
  23. }
  24. public override void Remove(Graphics g)
  25. {
  26. complexGraphicsList.Remove(g);
  27. }
  28. }

實際執行

  1. class MyClass
  2. {
  3. static void Main(string[] args)
  4. {
  5. ComplexGraphics complexGraphics = new ComplexGraphics("複雜圖形 - 兩條線段組成的複雜圖形");
  6. complexGraphics.Add(new Line("線段A"));
  7. complexGraphics.Add(new Line("線段C"));
  8. // 顯示複雜圖形的畫法
  9. Console.WriteLine("複雜圖形的繪製:");
  10. Console.WriteLine("---------------------");
  11. complexGraphics.Draw();
  12. Console.WriteLine("複雜圖形繪製完成");
  13. Console.WriteLine("---------------------");
  14. }
  15. }


優點:
組合模式介面都一致,可以存在容器統一處理

缺點:複雜度會增加


2017年2月1日 星期三

C# Template Pattern 樣板模式


遇到流程一樣,但是處理邏輯不一樣的狀況,可利用 Template Pattern 樣板模式

建立一個 UnitFlowBase 抽像類別提供框架,裡面有三個方法
Node1      (每個測試類別只都執行一次)
Node2      (每次執行測試方法時都執行一次)
....
UnitTest   (執行測試方法,裡面通常會執行多個 UnitFlowBase  提供的方法
所以才會說 UnitFlowBase 提供框架,例如:此範例  UnitTest 執行了 Node2() 與 Node3())

  1. public abstract class UnitFlowBase
  2. {
  3. protected UnitFlowBase()
  4. {
  5. Node1();
  6. }
  7. protected virtual void Node1()
  8. {
  9. }
  10. protected virtual void Node2()
  11. {
  12. }
  13. protected abstract bool Node3();
  14. public void UnitTest()
  15. {
  16. Node2();
  17. Console.WriteLine(Node3() ? "Assert Successful." : "Assert Fail.");
  18. }
  19. }

建立 UnitCounter1 與 UnitCounter2
Execute方法 顯示目前 ClassCount 跟 MethodCount 執行次數

  1. public class UnitCounter1 : UnitFlowBase
  2. {
  3. private int _classCounter = 0;
  4. private int _methodCounter = 0;
  5. protected override void Node1()
  6. {
  7. _classCounter++;
  8. }
  9. protected override void Node2()
  10. {
  11. _methodCounter++;
  12. }
  13. protected override bool Node3()
  14. {
  15. Console.WriteLine($"ClassCounter1 : {_classCounter}");
  16. Console.WriteLine($"MethodCounter1: { _methodCounter}");
  17. return true;
  18. }
  19. }
  20. public class UnitCounter2 : UnitFlowBase
  21. {
  22. private int _classCounter = 0;
  23. private int _methodCounter = 0;
  24. protected override void Node1()
  25. {
  26. _classCounter += 2;
  27. }
  28. protected override void Node2()
  29. {
  30. _methodCounter += 2;
  31. }
  32. protected override bool Node3()
  33. {
  34. Console.WriteLine($"ClassCounter2 : {_classCounter}");
  35. Console.WriteLine($"MethodCounter2: { _methodCounter}");
  36. return true;
  37. }
  38. }

建立一個 UnitCounter1 與 UnitCounter2 類別,各執行二次 UnitTest 方法

  1. class Program
  2. {
  3. static void Main(string[] args)
  4. {
  5. UnitCounter1 unit1_ = new UnitCounter1();
  6. UnitCounter2 unit2_ = new UnitCounter2();
  7. unit1_.UnitTest();
  8. unit1_.UnitTest();
  9. unit2_.UnitTest();
  10. unit2_.UnitTest();
  11. }
  12. }

執行結果:
ClassCount : 1
MethodCount : 1
Assert Successfull.
ClassCount : 1
MethodCount : 2
Assert Successfull.
ClassCount : 2
MethodCount : 2
Assert Successfull.
ClassCount : 2
MethodCount : 4
Assert Successfull.



















2017年1月2日 星期一

設計模式 Simple Factory 簡單工廠模式

情境:
要設計一個連接資料庫的物件,提供 MSSQL 與 MYSQL 兩種連線方式
讓使用者使用

 1.首先定一個介面
  1. public interface IDBConnection
  2. {
  3. void GetIDBConnection();
  4. }
  5.  
2.實作 MYSQL 與 MSSQL 資料庫連線方式
  1. public class MSSQL : IDBConnection
  2. {
  3. public void GetDBConnection()
  4. {
  5. Console.WriteLine("MYSQL連線 ");
  6. }
  7. }
  8. public class MYSQL:IDBConnection
  9. {
  10. public void GetDBConnection()
  11. {
  12. Console.WriteLine("MYSQL 連線");
  13. }
  14. }
  15.  
3.實做工廠類別
  1.  
  2. public class ConnectionFactory
  3. {
  4. public static IDDConnection GetConnection(DBType type)
  5. {
  6. IDDConnection db_ = null;
  7. switch (type)
  8. {
  9. case DBType.MySQL:
  10. db_ = new MYSQL();
  11. break;
  12. case DBType.MSSQL:
  13. db_ = new MSSQL();
  14. break;
  15. default:
  16. Console.WriteLine("default type");
  17. break;
  18. }
  19. return db_ ;
  20. }
  21. }
  22.  
  23.  
4.外部使用簡單工廠模式
  1.  
  2. class Program
  3. {
  4. static void Main(string[] args)
  5. {
  6. IDBConnection Connection_ = ConnectionFactory.GetConnection(DBType.MySQL);
  7. Connection.GetDBConnection();
  8. Console.ReadKey();
  9. }
  10. }
  11.  
  12.  

Blogger 顯示好看的程式碼內容(Code Block)

網路上查了一下 Code Block 發現 Google Code Prettify 看起來蠻順眼的, 就在這邊記錄一下設定的過程,留下筆記以便於日後查詢。

點選「版面配置」
在中間的位置點擊「新增小工具」

點擊「HTML/JavaScript」


























填入標題 Google Code Prettify

填入內容

選一個順眼的程式碼格式
Color themes for Google Code Prettify

使用方式如下














linenums 是顯示行號
language-cs 是指定 cs 的格式

範例程式碼

C# 二進制字串轉數值

範例:將字串 "1000000000000000000" 轉成 int 數值
  1.  
  2. using System;
  3. using System.Text.RegularExpressions;
  4. namespace BinaryStringToInteger
  5. {
  6. class Program
  7. {
  8. static void Main(string[] args)
  9. {
  10. BinaryStringToInteger("1000000000000000000");
  11. Console.ReadKey();
  12. }
  13. static readonly Regex Binary = new Regex("^[01]{1,32}$", RegexOptions.Compiled);
  14. static void BinaryStringToInteger(string s)
  15. {
  16. if (Binary.IsMatch(s))
  17. {
  18. Console.WriteLine(Convert.ToInt32(s, 2));
  19. }
  20. else
  21. {
  22. Console.WriteLine("invalid: " + s);
  23. }
  24. }
  25. }
  26. }
  27.  
  28.  

設計模式 C# 的 Singleton 的泛型


讓人繼承就可已變成 Singleton 獨體



  1. ///
  2. /// 給人繼承成為獨體
  3. ///
  4. /// 要繼承的類別
  5. public class Singleton where T : class, new()
  6. {
  7. protected Singleton()
  8. {
  9. Debug.Assert(null == _instance);
  10. }
  11. private static readonly T _instance = new T();
  12. public static T Instance
  13. {
  14. get
  15. {
  16. return _instance;
  17. }
  18. }
  19. }
  20.  
  21.  

Function Composition

由多個小 function 組合成大 function
  1. var john = new User("John", "Doe");
  2. string EmailTo(User u) => Domain(FullName(u));
  3. var email = EmailTo(john);
  4. Console.WriteLine(email);
  5. // jodo@gmail.com (姓 2 碼 + 名 2 碼 + gmail.com)

EmailFor() 能根據 User 的 姓 與 名 自動產生 email

string EmailTo(User u) => Domain(FullName(u));

EmailTo() 為 Local Function 由 FullName() 與 Domain() 組合出新的 EmailTo()

這就是所謂的 Function Composition

Function Composition 重複使用性高,程式碼必須『由右而左』

一般人『由左至右』的閱讀習慣,改用 Function Pipeline
  1. namespace ConsoleApp
  2. {
  3. public static class Email
  4. {
  5. public static string FullName(this User u) => ShortName(u.FirstName) + CutName(u.LastName);
  6. public static string Domain(this string localPart) => $"{localPart}@gmail.com";
  7. private static string ShortName(string s) => s.Substring(0, 2).ToLower();
  8. }
  9. }

將 Name() 與 Domain() 的第一個參數都改加上 this,變成為 Extension Method
  1. namespace ConsoleApp
  2. {
  3. internal static class Program
  4. {
  5. private static void Main()
  6. {
  7. var email =
  8. new User("John", "Doe")
  9. .FullName()
  10. .Domain();
  11. Console.WriteLine(email);
  12. }
  13. }
  14. }

原本的 EmailTo() Local Function 就不需要了
只要將 User new 後,直接用 FullName() 與 Domain(),維持了『由左至右』的閱讀習慣

只要將第一個參數加上 this 修飾成為 Extenstion Method 後,
就可由 Function Composition 改成 Function Pipeline 風格

Function Composition 與 Function Pipeline 講的是同一件事情,
只是 Function Composition 採用 由右至左,
而 Function Pipeline 符合閱讀習慣,採用 由左至右

Funciton Compostion 與 Function Pipeline 是 FP 關鍵部分,
以前總以為 C# 沒有支援,
因此無法使用 C# 寫 FP,
有了 Extension Method,
C# 就能很輕鬆的實踐 FP

Extension Method

目的:
讓我們在不修改原始程式,為類別增加新函式。

  1. Enumerable.Range(1, 3)
  2. .Select(x => x * 2)
  3. .ToList()
  4. .ForEach(x => Console.WriteLine(x.ToString()));

Enumerable.Range() 產生 1、2、3
Select() 2、4、6
ForEach() 列印在畫面上

因為 List 才有 ForEach()
所以先 ToList()
再 ForEach()

這邊可以利用 Extension Method
幫 IEnumerable 打造一個 ForEach()


  1. namespace ConsoleApp
  2. {
  3. public static class Extensions
  4. {
  5. internal static void ForEacht>(this IEnumerablet source, Actiont action)
  6. {
  7. foreach (T element in source)
  8. action(element);
  9. }
  10. }
  11. }

整理一下
第一、第一個參數 this IEnumerable 特別加了 this 且 class 或 interface
第二、Extension Method 必須都為 static method


  1. Enumerable
  2. .Range(1, 3)
  3. .Select(x => x * 2)
  4. .ForEach(x => Console.WriteLine(x.ToString()));
  1. 用了 Extension Method 後這樣就能拿掉 ToList()
  1.  

設計模式 AdpaterPattern 轉接器模式

情境:
目前已經有讀取資料的類別了
要設計一個讀取資料類別支援 Json 格式

1.目前已經有一個讀取資料的方法了

  1. public class FileReader
  2. {
  3. public string Read(string parameter)
  4. {
  5. string result = string.Empty;
  6. //實作硬碟讀取
  7. return result;
  8. }
  9. }
  10.  
  11.  

2.設計一個讀取資料的介面

  1. public interface IReadData
  2. {
  3. string GetJsonData(string parameter);
  4. }
  5.  
  6.  

3.繼承這個 IReadData 介面
  1. ///
  2. /// 從網路上讀取要的資料
  3. ///
  4. public class WebReader : IReadData
  5. {
  6. public string GetJsonData(string parameter)
  7. {
  8. string result = string.Empty;
  9. //實作網路讀取
  10. return result;
  11. }
  12. }
  13.  
  14.  

4.要讓 FileReader 共享 IReadData 介面 這時候轉接器模式就可以上場了
  1. public class FileAdapter : IReadData
  2. {
  3. public string GetJsonData(string parameter)
  4. {
  5. var reader = new FileReader();
  6. return reader.Read(parameter);
  7. }
  8. }
  9.  
  10.  

總結:
雖然 FileReader 在 FileAdapter 內
但是外部看起來 WebReader 與 FileAdapter 同樣繼承 IReadData 介面

Visual Studio 2017/2019 推薦的擴充功能與更新

參考文章: 覺得 Google 的 Blogger 不太順手?透過 HTML 的 iframe 移花接木 HackMD