2014年1月20日 星期一

C# - Generic泛型& Collections集合型別介紹

泛型是程式語言的一種特性。允許開發者在強型別程式語言中編寫代碼時定義一些可變部份,那些部份在使用前必須作出指明。各種程式語言和其編譯器、執行環境對泛型的支援均不一樣。


集合是將密切關聯的資料放在同一個集合中,可以更有效率地來處理這些資料。不需撰寫個別程式碼來處理各個別物件,您可以另行使用相同程式碼以處理集合的所有元素。


一般型別泛型型別
ArrayListList< >
QueueQueue< >
StackStack< >
HashtableDictionary< >
StoredListStoredList< >
ListDictionaryDictionary< >
HybridDictionaryDictionary< >
OrderedDictionaryDictionary< >
SortedDictionaryStoredDictionary< >
NameValueCollectionDictionary< >
DictionaryEntryKeyValPair< >
StringCollectionList<String>
StringDictionaryDictionary<String>

表格左邊一般型別,右邊則是對應的泛型型別,功能就跟一般型別差不多,使是用法不同罷了。假設當你需要撰寫一個功能/類別時,而無得知使用者會使用什麼型態的參數,有可能是string、也有可能是int,用泛型就不用擔心程式被呼叫時還要考慮傳入的型別,只要專心開發功能即可。
標準的集合,在功能上或執行的效率都有限制,System.Collections 命名空間
Hashtable用於包含10個或更多項目的集合,對小集合沒效率
SortedList依索引鍵來排序項目。
集合類別結構
DictionaryEntry主要是用來取出字典集合著Key和Value,可以利用它來逐一處理集合中的項目。
集合類別介面
IDictionary主要是用索引鍵來存取項目,不能用索引值。
IComparer自訂排序與順序的比較
IEqualityComparer只接受相等比較,自訂排序與順序的比較是由 IComparer 介面來提供
比較集合
CaseInsensitiveComparer比較兩個物件是否相等,忽略字串的大小寫。
特殊的集合,彌補標準字典的限制,System.Collections.Specialized 命名空間
ListDictionary用於包含10個或更少項目的集合,對大集合沒效率
HybridDictionary會先實作成ListDictionary的小集合,當內部變大時,將會把自己變成Hashtable的大集合。
OrderedDictionarySortedList類別多了一些功能,更為強大。
字串集合,只能使用字串,如果操作的對象不是物件而是字串,那這個類別是首選,因為它可以省下轉型的動作
StringDictionary索引鍵與資料值均採用強式型別宣告為字串,用法跟Hashtable相似
StringCollection用法跟ArrayList相似
NameValueCollectionStringDictionary相似,差別在於NameValueCollection可以單一索引鍵對應多重值。

CollectionsUtil建立忽略字串大小寫HashtableSortedList的集合。


如何使用泛型型別,以下就以List< >類別來做說明
1.匯入命名空間 System.Collections.Generic
using System.Collections.Generic;
2.引用泛型類別
List<string> myGen = new List<string>();
3.加入集合
myGen.Add("簡單");
myGen.Add("範例");      
4.輸出
foreach (string i in myGen)
{
     Console.WriteLine(i);

}

泛型並不困難,使用上跟一般型別沒什麼不同,
在C#底下就用<傳入的型別>,
例如:List<string> myGen = new List<string>( );

如何自定泛型類別
泛型類別宣告如同一般類別相似,只是宣告內容方法不一樣

泛型類別宣告:
class 類別名稱<T>[where 型別參數: 修件型別]{}

泛型方法宣告:
[存取限制修飾詞]返回型別 方法名稱(T|參數...)[where 型別參數: 絛件型別]{}
where T:struct限制為值型別
where T:class限制為參考型別
where T:new()可以使用 new T()[沒有參數之建構子]
where T:[base class]限制為類別的子類別
where T:[interface]限制必須實作介面
where T:U(U是型別參數)限制為U的子類別

1.宣告泛型類別
class 類別名稱<T1,T2,....Tn)>
{ 

}

其中以角括弧 < > 符號包住的 T1, T2,..., Tn 即是所謂的型別參數(type parameters),也就是先前提過的「變動的部分」。型別參數可有一至多個,端看實際的需要而定。慣例上,型別參數的名稱通常以一個大寫英文字母 ‘T’ 開頭,如果只有一個型別參數,通常就單純命名為 “T”,例如:List<T>。若有多個型別參數,則建議使用望文生義的名稱,以便識別。像 .NET 的泛型集合 Dictionary<TKey, TValue> 就很清楚,一看就知道這個泛型集合所要儲存的每一筆資料都包含兩種元素:key(索引鍵)與value(資料值),而這兩種元素的真正型別,則是等到實際要使用集合物件的時候才以參數的方式帶入。

2.宣告泛型方法

public class myGeneric<T>
{
    public void myTest(T myValue)
    {
        System.Type myType = myValue.GetType();
        Console.WriteLine(myType);
    }
}

3.引用泛型類別

myGeneric<int> myCom1 =new myGeneric<int>();
myCom1.myTest(1);
myGeneric<string> myCom2 = new myGeneric<string>();
myCom2.myTest("sdf");
myGeneric<double> myCom3 = new myGeneric<double>();

myCom3.myTest(12.2344);

不管引用任何型態,泛型類別都能全部接收。
運用泛型和集合,可以將資料類型作型別的分類,將特定型態的資料,透過集合來存取。
以前的傳統程式,要寫出這樣的功能,需要個別去寫出一個類別去使用,在.Net裡可以很輕易的就運用這樣的功能。

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

4 則留言:

  1. 不好意思可以請教您幾個問題嗎
    因為剛接觸C#這部分,所以發生了許多看不太懂的窘境,在網路上我看到了一個簡短的資料樹的方法
    namespace CSharpTree
    {
    public class TreeNode : IEnumerable>
    {

    public T Data { get; set; }
    public TreeNode Parent { get; set; }
    public ICollection> Children { get; set; }

    public TreeNode(T data)
    {
    this.Data = data;
    this.Children = new LinkedList>();
    }

    public TreeNode AddChild(T child)
    {
    TreeNode childNode = new TreeNode(child) { Parent = this };
    this.Children.Add(childNode);
    return childNode;
    }

    //other features ...
    }

    }

    這是簡短的部分,在引用部分當我跟著他使用了字串時
    TreeNode root = new TreeNode("root");
    {
    TreeNode node0 = root.AddChild("node0");
    TreeNode node1 = root.AddChild("node1");
    TreeNode node2 = root.AddChild("node2");
    {
    TreeNode node20 = node2.AddChild(null);
    TreeNode node21 = node2.AddChild("node21");
    {
    TreeNode node210 = node21.AddChild("node210");
    TreeNode node211 = node21.AddChild("node211");
    }
    }
    TreeNode node3 = root.AddChild("node3");
    {
    TreeNode node30 = node3.AddChild("node30");
    }
    }
    會出現無法使用建立出來的類別型式(root及它的內部方法AddChild)
    請問這是不是我遺漏了什麼導致不能夠使用了

    回覆刪除
  2. 不好意思可以請教您幾個問題嗎
    因為剛接觸C#這部分,所以發生了許多看不太懂的窘境,在網路上我看到了一個簡短的資料樹的方法
    namespace CSharpTree
    {
    public class TreeNode : IEnumerable>
    {

    public T Data { get; set; }
    public TreeNode Parent { get; set; }
    public ICollection> Children { get; set; }

    public TreeNode(T data)
    {
    this.Data = data;
    this.Children = new LinkedList>();
    }

    public TreeNode AddChild(T child)
    {
    TreeNode childNode = new TreeNode(child) { Parent = this };
    this.Children.Add(childNode);
    return childNode;
    }

    //other features ...
    }

    }

    這是簡短的部分,在引用部分當我跟著他使用了字串時
    TreeNode root = new TreeNode("root");
    {
    TreeNode node0 = root.AddChild("node0");
    TreeNode node1 = root.AddChild("node1");
    TreeNode node2 = root.AddChild("node2");
    {
    TreeNode node20 = node2.AddChild(null);
    TreeNode node21 = node2.AddChild("node21");
    {
    TreeNode node210 = node21.AddChild("node210");
    TreeNode node211 = node21.AddChild("node211");
    }
    }
    TreeNode node3 = root.AddChild("node3");
    {
    TreeNode node30 = node3.AddChild("node30");
    }
    }
    會出現無法使用建立出來的類別型式(root及它的內部方法AddChild)
    請問這是不是我遺漏了什麼導致不能夠使用了

    回覆刪除
  3. 不好意思可以請教您幾個問題嗎
    因為剛接觸C#這部分,所以發生了許多看不太懂的窘境,在網路上我看到了一個簡短的資料樹的方法
    namespace CSharpTree
    {
    public class TreeNode : IEnumerable>
    {

    public T Data { get; set; }
    public TreeNode Parent { get; set; }
    public ICollection> Children { get; set; }

    public TreeNode(T data)
    {
    this.Data = data;
    this.Children = new LinkedList>();
    }

    public TreeNode AddChild(T child)
    {
    TreeNode childNode = new TreeNode(child) { Parent = this };
    this.Children.Add(childNode);
    return childNode;
    }

    //other features ...
    }

    }

    這是簡短的部分,在引用部分當我跟著他使用了字串時
    TreeNode< string > root = new TreeNode("root");
    {
    TreeNode< string > node0 = root.AddChild("node0");
    TreeNode< string > node1 = root.AddChild("node1");
    TreeNode< string > node2 = root.AddChild("node2");
    {
    TreeNode< string > node20 = node2.AddChild(null);
    TreeNode< string > node21 = node2.AddChild("node21");
    {
    TreeNode< string > node210 = node21.AddChild("node210");
    TreeNode< string > node211 = node21.AddChild("node211");
    }
    }
    TreeNode< string > node3 = root.AddChild("node3");
    {
    TreeNode< string > node30 = node3.AddChild("node30");
    }
    }
    會出現無法使用建立出來的類別型式(root及它的內部方法AddChild)
    請問這是不是我遺漏了什麼導致不能夠使用了

    回覆刪除
  4. 你的語法在一開始繼承上就寫法不對,IEnumerable裡要使用的物件要明確的定義:
    TreeNode : IEnumerable>

    root無法使用建立出的類別表示它無法對應你所對應的資料型別,在root.AddChild("node0")裡可以看出,此資料是屬於字串型別,
    所以你在TreeNode的索引定義上要能指向可以定義的物件上:

    變數的定義寫法要注意:
    public T Data { get; set; }
    public TreeNode Parent { get; set; }
    public ICollection> Children { get; set; }

    在處理自身定義時要能呈現有彈性的處理:
    public TreeNode(T data)
    {
    this.Data = data;
    this.Children = new LinkedList>();
    this.ElementsIndex = new LinkedList>();
    this.ElementsIndex.Add(this);
    }

    大致上的概念就是這樣。

    回覆刪除