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 的用法



sealed public class S3Mgr
{
 ....
}



只要繼承 S3Mgr 就會編譯錯誤







partial class 將 class 分成多個 cs 檔

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



// A.cs 檔
partial class Program
{
public A _a = new A();
....
}




// B.cs 檔
partial class Program
{
public B _a = new B();
....
}


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

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



C# Composite Pattern 組合模式


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



/// <summary>
/// 圖形抽像類
/// </summary>
public abstract class Graphics
{
 public string Name { get; set; }
 public Graphics(string name)
 {
  this.Name = name;
 }

 public abstract void Draw();
 public abstract void Add(Graphics g);
 public abstract void Remove(Graphics g);
}


簡單圖形



/// <summary>
/// 簡單圖形類——線
/// </summary>
public class Line : Graphics
{
 public Line(string name)
  : base(name)
 { }

 // 重寫父類抽像方法
 public override void Draw()
 {
  Console.WriteLine("畫線:" + Name);
 }

 public override void Add(Graphics g)
 {
  throw new Exception("不能向簡單圖形Line添加其他圖形");
 }
 public override void Remove(Graphics g)
 {
  throw new Exception("不能向簡單圖形Line移除其他圖形");
 }
}

/// <summary>
/// 簡單圖形類——圓
/// </summary>
public class Circle : Graphics
{
 public Circle(string name)
  : base(name)
 { }

 // 重寫父類抽像方法
 public override void Draw()
 {
  Console.WriteLine("畫圓:" + Name);
 }

 public override void Add(Graphics g)
 {
  throw new Exception("不能向簡單圖形Circle添加其他圖形");
 }
 public override void Remove(Graphics g)
 {
  throw new Exception("不能向簡單圖形Circle移除其他圖形");
 }
}


複雜圖形



/// <summary>
/// 複雜圖形,由一些簡單圖形組成,假設該複雜圖形由兩條線組成
/// </summary>
public class ComplexGraphics : Graphics
{
 private List<Graphics> complexGraphicsList = new List<Graphics>();

 public ComplexGraphics(string name)
  : base(name)
 { }

 /// <summary>
 /// 複雜圖形的畫法
 /// </summary>
 public override void Draw()
 {          
  foreach (Graphics g in complexGraphicsList)
  {
   g.Draw();
  }
 }

 public override void Add(Graphics g)
 {
  complexGraphicsList.Add(g);
 }
 public override void Remove(Graphics g)
 {
  complexGraphicsList.Remove(g);
 }
}


實際執行



class MyClass
{
 static void Main(string[] args)
 {
  ComplexGraphics complexGraphics = new ComplexGraphics("複雜圖形 - 兩條線段組成的複雜圖形");
  complexGraphics.Add(new Line("線段A"));
  complexGraphics.Add(new Line("線段C"));

  // 顯示複雜圖形的畫法
  Console.WriteLine("複雜圖形的繪製:");
  Console.WriteLine("---------------------");
  complexGraphics.Draw();
  Console.WriteLine("複雜圖形繪製完成");
  Console.WriteLine("---------------------");
 }
}



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

缺點:複雜度會增加


2017年2月1日 星期三

C# Template Pattern 樣板模式


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

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



public abstract class UnitFlowBase
{
    protected UnitFlowBase()
    {
        Node1();
    }

    protected virtual void Node1()
    {
    }

    protected virtual void Node2()
    {
    }

    protected abstract bool Node3();

    public void UnitTest()
    {
        Node2();
        Console.WriteLine(Node3() ? "Assert Successful." : "Assert Fail.");
    }
}


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



public class UnitCounter1 : UnitFlowBase
{
    private int _classCounter = 0;

    private int _methodCounter = 0;

    protected override void Node1()
    {
        _classCounter++;
    }

    protected override void Node2()
    {
        _methodCounter++;
    }

    protected override bool Node3()
    {
        Console.WriteLine($"ClassCounter1 : {_classCounter}");
        Console.WriteLine($"MethodCounter1: { _methodCounter}");

        return true;
    }
}

public class UnitCounter2 : UnitFlowBase
{
    private int _classCounter = 0;

    private int _methodCounter = 0;

    protected override void Node1()
    {
        _classCounter += 2;
    }

    protected override void Node2()
    {
        _methodCounter += 2;
    }

    protected override bool Node3()
    {
        Console.WriteLine($"ClassCounter2 : {_classCounter}");
        Console.WriteLine($"MethodCounter2: { _methodCounter}");

        return true;
    }
}


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



class Program
{
    static void Main(string[] args)
    {
        UnitCounter1 unit1_ = new UnitCounter1();
	UnitCounter2 unit2_ = new UnitCounter2();
        unit1_.UnitTest();
        unit1_.UnitTest();
        unit2_.UnitTest();
	unit2_.UnitTest();
    }
}


執行結果:
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.首先定一個介面

public interface IDBConnection
{
    void GetIDBConnection();
}

2.實作 MYSQL 與 MSSQL 資料庫連線方式

public class MSSQL : IDBConnection
{
    public void GetDBConnection()
    {
        Console.WriteLine("MYSQL連線 ");
    }
}

public class MYSQL:IDBConnection
{
    public void GetDBConnection()
    {
        Console.WriteLine("MYSQL 連線");
    }
}

3.實做工廠類別


public class ConnectionFactory
{
    public static IDDConnection GetConnection(DBType type) 
    {
        IDDConnection db_ = null;
        switch (type)
        {
            case DBType.MySQL:
                db_ = new MYSQL();
                break;
            case DBType.MSSQL:
                db_ = new MSSQL();
                break;
            default:
                Console.WriteLine("default type");
                break;
        }
        return db_ ;
    }
}


4.外部使用簡單工廠模式


class Program
{
    static void Main(string[] args)
    {
        IDBConnection Connection_ = ConnectionFactory.GetConnection(DBType.MySQL);
        Connection.GetDBConnection();
        Console.ReadKey();
    }
}


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 數值



using System;
using System.Text.RegularExpressions;

namespace BinaryStringToInteger
{
    class Program
    {
        static void Main(string[] args)
        {
            BinaryStringToInteger("1000000000000000000");

            Console.ReadKey();
        }

        static readonly Regex Binary = new Regex("^[01]{1,32}$", RegexOptions.Compiled);
        static void BinaryStringToInteger(string s)
        {
            if (Binary.IsMatch(s))
            {
                Console.WriteLine(Convert.ToInt32(s, 2));
            }
            else
            {
                Console.WriteLine("invalid: " + s);
            }
        }
    }
}



設計模式 C# 的 Singleton 的泛型


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





        /// 
        /// 給人繼承成為獨體
        /// 
        /// 要繼承的類別
        public class Singleton where T : class, new()
        {
            protected Singleton()
            {
                Debug.Assert(null == _instance);
            }
            private static readonly T _instance = new T();

            public static T Instance
            {
                get
                {
                    return _instance;
                }
            }
        }



Function Composition

由多個小 function 組合成大 function


var john = new User("John", "Doe");
string EmailTo(User u) => Domain(FullName(u));

var email = EmailTo(john);

Console.WriteLine(email);
// 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


namespace ConsoleApp
{
    
    public static class Email
    {
        public static string FullName(this User u) => ShortName(u.FirstName) + CutName(u.LastName);

        public static string Domain(this string localPart) => $"{localPart}@gmail.com";
        private static string ShortName(string s) => s.Substring(0, 2).ToLower();
    }
}


將 Name() 與 Domain() 的第一個參數都改加上 this,變成為 Extension Method


namespace ConsoleApp
{
    internal static class Program
    {
        private static void Main()
        {
            var email = 
                new User("John", "Doe")
                    .FullName()
                    .Domain();
            
            Console.WriteLine(email);
        }
    }
}

原本的 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

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



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


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

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

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




namespace ConsoleApp
{
    public static class Extensions
    {
        internal static void ForEach<t>(this IEnumerable<t> source, Action<t> action)
        {
            foreach (T element in source)
                action(element);
        }
    }
}


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




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


用了 Extension Method 後這樣就能拿掉 ToList()

設計模式 AdpaterPattern 轉接器模式

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

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



public class FileReader
{
    public string Read(string parameter)
    {
        string result = string.Empty;
        //實作硬碟讀取
        return result;
    }
}




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



public interface IReadData
{
    string GetJsonData(string parameter);
}




3.繼承這個 IReadData 介面


/// 
/// 從網路上讀取要的資料
/// 
public class WebReader : IReadData
{
    public string GetJsonData(string parameter)
    {
        string result = string.Empty;
        //實作網路讀取
        return result;
    }
}




4.要讓 FileReader 共享 IReadData 介面 這時候轉接器模式就可以上場了


public class FileAdapter : IReadData
{
    public string GetJsonData(string parameter)
    {
        var reader = new FileReader();
        return reader.Read(parameter);
    }
}




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

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

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