2019年9月27日 星期五

C# Generics泛型和C++的泛型



前言這種事情,當然就要把微軟這名號先搬出來先,先看看微軟官方文件裡的文字說明(沒錯,我就是不負責A_____A有問題請找微軟)


「泛型是在 C# 語言和 Common Language Runtime (CLR)  2.0 版中加入的功能。 泛型將型別參數的概念引進 .NET Framework 中,使得類別和方法在設計時,可以先行擱置一個或多個型別規格,直到用戶端程式碼對類別或方法進行宣告或執行個體化時再行處理。」

public class GenericList<T>
{
    void Add(T input) { }
}

class TestGenericList
{
    private class ExampleClass { }
    static void Main()
    {
        GenericList<int> list1 = new GenericList <int>();
        GenericList<string> list2 = new GenericList<string>();
        GenericList<ExampleClass> list3 = new GenericList<ExampleClass>();
    }
}

以下是微軟對泛型的定義和概觀

使用泛用型別以最佳化程式碼重複使用、型別安全性和效能。
泛型的最常見用法是建立集合類別。
.NET Framework 類別庫包含 System.Collections.Generic 命名空間中的數種新泛用集合類別。 在任何可能的情況下都應該使用這些類別,而不是使用類似在 System.Collections 命名空間中的 ArrayList 類別。
可以建立自己的泛用介面、類別、方法、事件和委派 (Delegate)
泛用類別可能會被限制為啟用對特定資料型別上的方法進行存取。
在泛型資料型別中所使用型別的資訊,可以在執行階段透過反映 (Reflection) 取得。


看完了上面這麼長連我也看不懂的內容時,看倌有了解上面的意思嗎?
如果有,那麼恭喜你啦,如果你跟我一樣沒有的,那就來聽聽我的故事。

有一天老闆和我說:「昨天我說的Excel清單你有做了嗎?」我就說有,然後我就把我做好Excel清單給他,他跟我說,不是客戶清單,是要購買的項目清單。


於是我突然靈光一動,就想到了「泛型」(因為清單可以是代表各種類型的清單)(有人會想說:「為什麼我會想到泛型啦XDDD。」)

因為早期在寫一段判斷時,如果在強行別的情況下,常常會因為使用上的限制而讓程式碼寫的很長,很難維護,而且每次要針對不同的型別去各別定義。
不過資訊在進步、程式在爆炸、硬體在飛天的情況下,現在寫程式都有很多新的概念,因為除非你是一個寫低階韌體程式的人,要不然現在記憶體動不動就GG去,CPU動不動就在24核的時代裡,現在在用很古板的程式寫法的話,就很浪費時間。所以泛型的出現,就是為了這個而誕生的。(至少我是這麼認為啦XDDDDDDD如果有高手有不同想法,先別炮我啊啊啊)

如果可以透過泛型的方式來達成,不管是哪一種清單,都會自動的去對應的,那這就是泛型的主要目的。

泛型(Generic Type)它可以讓你在定義ClassMethodInterface時先不用決定型別,到了要實體化的時候再決定其型別,這在集合的應用上更為重要。目的就是要維護「型別」的安全性,讓建構集合(set)時的錯誤能在編譯期間就抓到,不會等執行時才發生錯誤。

所以「泛型」,更白話一點的說法,就是可以泛用通用的類型,簡單來說就是用來增加彈性,且可以明確指定型別是哪一種型別,是不是感覺就猛猛的。

舉例來說,現在的ArrayList就是泛型早期的一種應用

ArrayList list = new ArrayList ();
list.Add(new object());

Add裡塞入的一個object,它可以是任何型別,不過當要對應任何的元素時,要對每一項都進行轉型,那程式碼會變的很冗長。所以就開始讓這個做法定義一個名稱「Generic」,並列入正宮之列。

不過定義都是人給的,每個程式語言的特性都不同,自然對泛型的使用定義上也會有不同,為什麼我會想要拿C++C#來比較呢???

因為我爽XDDDDDDD

其實是因為有很多人會思考C#有部分是參考C++的原型而來C11之後有支援實質的泛型,那它們在語法使用上是不是都很相似呢????



下面是一個C++的例子:

#include <iostream>
using namespace std;
template <typename T>
T add(T aT b)
{
           return a + b;
}
void main()
{
           int num1, num2, sum;
           cin >> num1 >> num2;
           sum = add(num1, num2);
           cout << sum;
}

上面有看到,在C++裡,有一個template,在官方的說明中是這樣的:
template範本是一種建構,在根據使用者所提供的範本參數的引數的編譯時期產生的一般型別或函式。」

template <typename T>
T minimum(const Tlhsconst Trhs)
{
           return lhs < rhs ? lhs : rhs;
}

上述程式碼說明具有單一類型參數的泛型函式樣板T,其傳回值,並呼叫參數 (lhs  rhs) 全都是這個型別。 您可以類似,但依慣例單一大寫的字母您最常使用的任何項目名稱的型別參數。 T是範本參數,而typename關鍵字指出此參數類型的預留位置。 呼叫此函式時,編譯器將會取代每個執行個體T與所指定使用者或編譯器推算出的具象型別引數。 處理程序,編譯器會產生的類別,或從範本函式指樣板具現化;minimum<int>範本的具現化minimum<T>


以上的官方說明(是官方說的喔XDDD)我們可以知道,template也是實作了泛型的原理概念,附上連結可以去看看更完整的內容:

微軟-樣板 (C++)


微軟也許想要跟C++有區別,在使用上微軟官方有說明其差異性:

C++ 樣板和 C# 泛型之間的差異 (C# 程式設計手冊)



從上面來看的話,會了解有怎樣的差異呢??

假設,我想在C中做這樣的事:

class D<T>
{
    class S { }
    D<D<T>.S> ds;
}

C#泛型中執行的話沒問題,在運行時,類型僅為所有引用類型參數構建一次。

但是在C的結構之下呢,當你有D< int>時會發生什麼?內部類型構造D< D< int> .S>類型的字段,因此我們需要構造該類型.但是該類型建構類型為D< D< D< int> .S> .S>等的字段,依此類推為無窮大。

class Program
{
    static void Main(string[] args)
    {
        C.DoIt<string>("Mike");
    }
}

public class C
{
    public static void DoIt<T>(T t)
    {
        ReallyDoIt(t);
    }

    public static void ReallyDoIt(string s)
    {
        System.Console.WriteLine("Natural version: " + s);
    }

    public static void ReallyDoIt<T>(T t)
    {
        System.Console.WriteLine("Generic version: " + t);
    }
}

C++ 編譯器在template時,會根據實際需要的參數型別產生多種版本的類別代碼,也就是說,實際編譯出來的全都是可直接繫結的真實型別。但 C# 的泛型就只是泛型;編譯器並不會因為你在呼叫泛型 method 時分別用到了 string  int 兩種參數而編譯出兩種版本的 method 代碼。

有些人在接觸了這兩種程式語言時,可能會以「泛型(generic)其實就是樣板(template)型別」來理解和解釋泛型,但C# 的泛型和 C++ 的樣板其實骨子裡是不同的。


-雲遊山水為知已逍遙一生而忘齡電腦神手

2019年9月26日 星期四

C# Design Pattern - Composite 組合模式


Composite,單字的意思就是「綜合、合成的、合成物……..(想不到了)」,意思是一樣東西,他可以有許多不同的元素,或者功能,或者是特性等等。

所以我們在實現在Composite這個Design Pattern的時候,只要往這固方向去思考,其實可以找到一個方向,是不是這麼說的??是不是是不是是不是。

好,接下來就用你我的想像力,來了喔,要想像,我們想像我們在寫一個軟體時候,想一個方向是:圖像的本質,也就是圖像XDDD。
所以圖像就可以呈現任何的可能,可以是一隻鳥,一個正妹,一個帥氣的我(無誤)。

然後我們就定義一個名稱叫「Graphics」,然後它可以做的事情,就是Draw,像這樣:

public abstract class Graphics
{
    protected string _name;

    public Graphics(string name)
    {
        this._name = name;
    }

    public abstract void Draw();
}

然後我們再製造一樣東西和「Graphics」很相似的東西,但更具體,就叫它「Picture」。「Picture」它可以有和「Graphics」相似的元素,然後可以實現更具體的事情,它一樣可以畫圖。然後一樣複製了相同相以的東西,如「Line」、「Circle」、「Rectangle」。但它面都和「Picture」一樣,可以有相似的特性和元素,接著我們就實現這樣的程式結構:

public class Picture : Graphics
{
    public Picture(string name) : base(name) { }

    public override void Draw() { Console.WriteLine("Draw Picture Role Name" + _name.ToString()); }

    public ArrayList GetChilds()
    {

        //返回所有的子物件
        ArrayList a = new ArrayList();
        return a;

    }
}

public class Line : Graphics
{

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

    public override void Draw()
    {

        Console.WriteLine("Draw a" + _name.ToString());

    }

}

public class Circle : Graphics
{
    public Circle(string name) : base(name) { }
    public override void Draw()
    {
        Console.WriteLine("Draw Circle role" + _name.ToString());
    }
}



public class Rectangle : Graphics
{
    public Rectangle(string name) : base(name) { }

    public override void Draw()
    {
        Console.WriteLine("Draw Rectangle role" + _name.ToString());
    }
}

然後我們就可以依序將每個相似特性執行看看。
Picture picture = new Picture("Picture");
picture.Draw();

Line line = new Line("Line");
line.Draw();

Circle circle = new Circle("Circle");
circle.Draw();

Rectangle rectangle = new Rectangle("Rectangle");
rectangle.Draw();


看到下面呈現的結果後,我們可以發現,這四個類別都有非常相似的特性:




但這樣如果分散著寫的話,就會覺得有點笨,畢竟時代在進步,寫程式的Level也要提升。
這時候我們要對結構做一點加工,將一些相同基本圖像元素(直線、圓等)以及一些複合圖像元素(由基本圖像元素組合而成)建構一起。在設計中對每一個物件都配一個Draw()方法,在調用時,會顯示相關的圖形。可以看到這裡複合圖像元素它在充當物件的同時,又是那些基本圖像元素的一個容器。

public abstract class Graphics
{
    protected string _name;
    public Graphics(string name)
    {
        this._name = name;
    }

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

}

要對相似圖像元素進行處理,在程式中,需要判斷返回物件的具體類型到底是基本圖像元素,還是複合圖像元素。如果是複合圖像元素,要用遞迴去處理, 然而這種處理的結果卻增加了用戶端程式與複雜圖像元素內部結構之間的依賴,這會程式在執行時看起來很笨拙,會有什麼樣的情況這邊在此先不討論,之後會延伸話題,不過在此說明,其實程式沒有一定最標準的寫法,任何一種技巧都存在著不同情境,不同效果、不同的商業需求而產生的優勢與劣勢。

如上面的程式,讓程式可以像處理基本圖像元素一樣來處理複合圖像元素,這就要引入Composite模式了,需要把對於子物件的管理工作交給複合圖像元素,為了進行子物件的管理,必須提供必要的Add()Remove()等方法(名稱是可以自行定義的,想定什麼可以天馬行空的去想)

public abstract class Graphics
{
    protected string _name;
    public Graphics(string name)
    {
        this._name = name;
    }

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

public class Picture : Graphics
{
    protected ArrayList picList = new ArrayList();
    public Picture(string name) : base(name) { }

    public override void Draw()
    {
        Console.WriteLine("Draw Picture" + _name.ToString());
        foreach (Graphics g in picList)
        {
            g.Draw();
        }
    }

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

public class Line : Graphics

{
    public Line(string name) : base(name) { }
    public override void Draw()
    {
        Console.WriteLine("Draw Line role" + _name.ToString());
    }
    public override void Add(Graphics g) { }
    public override void Remove(Graphics g) { }
}

public class Circle : Graphics
{
    public Circle(string name) : base(name) { }
    public override void Draw()
    {
        Console.WriteLine("Draw Circle role" + _name.ToString());
    }

    public override void Add(Graphics g) { }
    public override void Remove(Graphics g) { }
}

public class Rectangle : Graphics
{
    public Rectangle(string name) : base(name) { }
    public override void Draw()
    {
        Console.WriteLine("Draw Rectangle role" + _name.ToString());
    }

    public override void Add(Graphics g) { }
    public override void Remove(Graphics g) { }
}

要對圖像元素進行處理,在程式裡需要判斷返回物件的具體類型到底是基本圖像元素,還是複合圖像元素,複合,就是多重且有重覆的意思,所以就是多重重覆的圖像元素(解釋完畢)。如果是複合圖像元素,要用遞迴去處理, 但這種處理的結果會增加了在建構程式時產生複雜結構,重覆的語句會變的太多。要讓程式可以像處理基本圖像元素一樣來處理複合圖像元素,這就要引入Composite模式了,需要把對於子物件的管理工作交給複合圖像元素,為了進行子物件的管理,它必須提供必要的Add()Remove()等方法。

看一下下面的例子(要好好看啊):

public class Picture : Graphics
{
    protected ArrayList picList = new ArrayList();
    public Picture(string name) : base(name) { }

    public override void Draw()
    {
        Console.WriteLine("Draw Picture" + _name.ToString());

        foreach (Graphics g in picList)
        {
            g.Draw();
        }
    }

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

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

public class Line : Graphics
{
    public Line(string name) : base(name) { }
    public override void Draw()
    {
        Console.WriteLine("Draw Line role" + _name.ToString());
    }

    public override void Add(Graphics g) { }
    public override void Remove(Graphics g) { }
}

public class Circle : Graphics
{
    public Circle(string name) : base(name) { }
    public override void Draw()
    {
        Console.WriteLine("Draw Circle role" + _name.ToString());
    }

    public override void Add(Graphics g) { }
    public override void Remove(Graphics g) { }
}



public class Rectangle : Graphics
{
    public Rectangle(string name) : base(name) { }

    public override void Draw()
    {
        Console.WriteLine("Draw Rectangle role" + _name.ToString());
    }

    public override void Add(Graphics g) { }
    public override void Remove(Graphics g) { }
}

這樣引入Composite模式後,程式不再依賴複合圖像元素的內部方法,執行時程式結構就不用那麼牛笨。我們可以再讓程式更加進化:

public abstract class Graphics
{
    protected string _name;

    public Graphics(string name)
    {
        this._name = name;
    }
    public abstract void Draw();
}

public class Picture : Graphics
{
    protected ArrayList picList = new ArrayList();

    public Picture(string name)
        : base(name)
    { }
    public override void Draw()
    {
        Console.WriteLine("Draw Picture:" + _name.ToString());

        foreach (Graphics g in picList)
        {
            g.Draw();
        }
    }

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

public class Line : Graphics
{
    public Line(string name)
        : base(name)
    { }

    public override void Draw()
    {
        Console.WriteLine("Draw role" + _name.ToString());
    }
}

public class Circle : Graphics
{
    public Circle(string name)
        : base(name)
    { }

    public override void Draw()
    {
        Console.WriteLine("Draw role:" + _name.ToString());
    }
}

public class Rectangle : Graphics
{
    public Rectangle(string name)
        : base(name)
    { }

    public override void Draw()
    {
        Console.WriteLine("Draw Role" + _name.ToString());
    }
}

這種方式屬於安全式的Composite模式,通過Composite模式,程式在調用Draw()的時候不用再去判斷複雜圖像元素中的子物件到底是基本圖像元素,還是複雜圖像元素,最後是實現它:



最後附上此文章的結構圖說明:





在這文章,我盡量用比較簡單的方式去解說,當然上面的例子在網址請Google大神來搜尋的話,真的是一大堆,我用自已理解的方式再轉變成我的表達方式。相互激撞可以有不同的火花XDDDDDD。


-雲遊山水為知已逍遙一生而忘齡- 電腦神手