黑馬程序員-.NET基礎之泛型

------- Windows Phone 7手機開發.Net培訓、期待與您交流! -------

 

泛型引例ArrayList

  相信大家在看楊中科老師基礎視頻中都不陌生個類ArrayList,我的基礎測試中也有關於這個類運用的一道題。可見泛型是很基礎同時又是很重要的一個知識點。ArrayList通用化是通過在類型與通用基類型Object之間進行強制轉換來實現的:添加到ArrayList中的任何引用或值類型都將隱式地向上強制轉換爲Object;如果項是值類型,則添加時需要進行裝箱操作,檢索時需要進行拆箱操作。
ArrayList通用化有2個主要缺點:強制轉換以及裝箱和拆箱操作都會降低性能(特別是大型集合時);另一個限制是缺少編譯時類型檢查,因爲所有項都強制轉換爲Object,所以在編譯時無法防止客戶端代碼執行非法操作。

using System;
using System.Collections;
namespace CSharpPractice.ArrayList
{
    public class ArrayListTest
    {
        public static void Main()
        {
            //創建整型數組列表
            ArrayList list1 = new ArrayList();
            list1.Add(3);
            list1.Add(105);
            //顯示整型數組列表中的值
            Console.WriteLine("整型數組列表ArrayList1的內容如下:");
            foreach (int x in list1)
            {
                Console.WriteLine(x);
            }

            //創建字符串型數組列表
            ArrayList list2 = new ArrayList();
            list2.Add("Hello");
            list2.Add("Friends");
            //顯示字符串型數組列表中的值
            Console.WriteLine("字符串型數組列表ArrayList2的內容如下:");
            foreach (string s in list2)
            {
                Console.WriteLine(s);
            }

            //創建整數、字符串混合型數組列表
            ArrayList list3 = new ArrayList();
            list3.Add(11);       // 添加一個整數.
            list3.Add("字符串"); // 添加一個字符串
            int t = 0;
            // 求數組列表ArrayList3各元素之和
            Console.WriteLine("數組列表ArrayList3各元素之和爲:");
            foreach (int x in list3) // 產生運行時錯誤:InvalidCastException
            {
                t += x;
            }
            Console.ReadLine();
        }
    }
}


 

引例List<T>

   與ArrayList相比,使用 List<T>時,必須爲每個實例指定其具體的數據類型。這樣將不再需要向上強制轉換爲System.Object以及裝箱和拆箱操作,同時也使得編譯器可以進行類型檢查,從而解決了ArrayList通用化的2個主要問題,保證了程序的性能和健壯性。

using System;
using System.Collections;
using System.Collections.Generic;
namespace CSharpPractice.ListT
{
    public class ListTest
    {
        public static void Main()
        {
            //創建整型數組列表
            List<int> list1 = new List<int>();
            list1.Add(3);
            list1.Add(105);
            //顯示整型數組列表中的值
            Console.WriteLine("整型數組列表ArrayList1的內容如下:");
            foreach (int x in list1)
            {
                Console.WriteLine(x);
            }

            //創建字符串型數組列表
            List<string> list2 = new List<string>();
            list2.Add("Hello");
            list2.Add("Friends");
            //顯示字符串型數組列表中的值
            Console.WriteLine("字符串型數組列表ArrayList2的內容如下:");
            foreach (string s in list2)
            {
                Console.WriteLine(s);
            }

            // 試圖創建整數、字符串混合型數組列表,失敗!
            List<int> list3 = new List<int>();
            list3.Add(11);         // 添加一個整數.
            //list3.Add("字符串");  // 編譯錯誤
            list3.Add(22);        //  添加另一個整數.
            int t = 0;
            Console.WriteLine("數組列表ArrayList3各元素之和爲:");
            foreach (int x in list3)
            {
                t += x;
            }
            Console.WriteLine(t);
            Console.ReadLine();
        }
    }
}


 

一、泛型的概念與定義

    泛型類似於 C++ 模板,通過泛型可以定義類型安全的數據結構,而無須使用實際的數據類型。例如,通過定義泛型方法(static void Swap<T>(ref T lhs, ref T rhs)),可以重用數據處理算法,實現不同類型數據(例如int、double)的交換,而無需分別爲int和double複製類型特定的代碼(重載方法),從而顯著提高性能並得到更高質量的代碼。.NET Framework 2.0版類庫提供一個新的命名空間 System.Collections.Generic,其中包含若干基於泛型的集合類。在泛型類的聲明中,需要聲明泛型參數,然後在泛型類的成員聲明中,使用該泛型參數作爲通用類型;而在創建泛型類的實例時,則需要與泛型參數對應的實際類型。

2.泛型類型參數

   在泛型類型定義中,必須通過指定尖括號中的類型參數來聲明類型。類型參數實際上並不是特定類型,而只是類型佔位符。在創建泛型類型的實例時,必須指定尖括號中的類型(可以是編譯器識別的任何類型)
例如:
GenericList<float> list1 = new GenericList<float>();
GenericList<ExampleClass> list2 = new GenericList<ExampleClass>();
GenericList<ExampleStruct> list3 = new GenericList<ExampleStruct>();
類型參數遵循下列命名準則
使用“T”作爲描述性類型參數名的前綴,並使用描述性名稱命名泛型類型參數。

using System;
using System.Collections.Generic;
namespace CSharpPractice.Generic
{
    public class GenericList<T>
    {
        // 嵌套類也通過泛型參數(<T>)定義.
        private class Node
        {   // T用於非泛型構造函數.
            public Node(T t)
            {
                next = null;
                data = t;
            }
            private Node next;
            public Node Next
            {
                get { return next; }
                set { next = value; }
            }
            // T作爲數據類型私有成員.
            private T data;
            // T作爲屬性返回類型.
            public T Data
            {
                get { return data; }
                set { data = value; }
            }
        }
        private Node head;
        public GenericList()  // 構造函數
        {   // 堆棧(後進先出)初始化
            head = null;
        }
        // T作爲方法的參數類型:
        public void AddHead(T t)
        { // 新結點加入到當前堆棧的頭部,並且成爲head
            Node n = new Node(t);
            n.Next = head;
            head = n;
        }
        public IEnumerator<T> GetEnumerator()
        {  // 從頭到尾依次輸出堆棧中的值
            Node current = head;
            while (current != null)
            {
                yield return current.Data;
                current = current.Next;
            }
        }
    }
    class TestGenericList
    {
        static void Main()
        {  // int是類型參數
            GenericList<int> list = new GenericList<int>();
            Console.WriteLine("0~9 十個整數形成堆棧");
            for (int x = 0; x < 10; x++)
            {  // 0~9 十個整數形成堆棧
                list.AddHead(x);
            }
            Console.WriteLine("堆棧(後進先出)內容如下:");
            foreach (int i in list)
            {   // 輸出堆棧內容9~0
                Console.Write(i + " ");
            }
            Console.ReadLine();
        }
    }
}


 

二、泛型接口

   在泛型類的設計中,通常也把泛型類共通要實現的方法、委託或事件的簽名封裝爲泛型接口,然後在實現這些泛型接口的泛型類中實現這些方法等,下面是泛型接口示例。

using System;
using System.Collections.Generic;
namespace CSharpPractice.GenericInterface
{
    class Program
    {
        static void Main()
        {
            int[] arr = { 0, 1, 2, 3, 4 };
            List<int> list = new List<int>();

            for (int x = 5; x < 10; x++)
            { // 形成列表 5、6、7、8、9
                list.Add(x);
            }

            Console.WriteLine("輸出數組列表ArrayList的內容:");
            ProcessItems<int>(arr);
            Console.WriteLine("輸出列表List的內容:");
            ProcessItems<int>(list);
            Console.ReadLine();

        }

        static void ProcessItems<T>(IList<T> coll)
        {
            foreach (T item in coll)
            {
                Console.Write(item.ToString() + " ");
            }
            Console.WriteLine();
        }
    }
}

 

三、泛型方法

泛型方法是使用類型參數聲明的方法。編譯器能夠根據傳入的方法實參推斷類型形參。

using System;
namespace CSharpPractice.GenericMethod
{
    public class GenericMethod
    {
        //聲明泛型方法:兩者交換
        static void Swap<T>(ref T lhs, ref T rhs)
        {
            T temp;
            temp = lhs;
            lhs = rhs;
            rhs = temp;
        }
        public static void Main()
        {
            int a = 1;
            int b = 2;
            Console.WriteLine("Original value, a = {0} , b = {1}", a, b);
            //調用泛型方法:指定泛型參數的類型
            Swap<int>(ref a, ref b);
            Console.WriteLine("After swapping, a = {0} , b = {1}", a, b);
            //調用泛型方法:可以省略類型參數,編譯器將推斷出該參數
            double c = 1.1d;
            double d = 2.2d;
            Console.WriteLine("Original value, c = {0} , d = {1}", c, d);
            Swap(ref c, ref d);
            Console.WriteLine("After swapping, c = {0} , d = {1}", c, d);
            Console.ReadLine();
        }
    }
}

 

 

四、泛型委託和泛型事件

通過泛型類型參數,同樣可以定義泛型委託。通過指定類型參數,可以引用泛型委託
在泛型類內部定義的委託,可以使用泛型類的泛型類型參數
基於泛型委託,可以定義泛型事件。此時發送方參數可以爲強類型,不再需要強制轉換成Object,或反向強制轉換,下面給出泛型委託和事件示例。

using System;
namespace CSharpPractice.GenericIntegrated
{
    // 類型參數T使用尖括號< >括起.
    public class GenericList<T> : System.Collections.Generic.IEnumerable<T>
    {
        protected Node head;
        protected Node current = null;
        // 嵌套類也是關於T的泛型類
        protected class Node
        {
            public Node next;
            private T data;    // T作爲私有成員數據類型
            public Node(T t)  // T用於非泛化構造函數
            {
                next = null;
                data = t;
            }
            public Node Next
            {
                get { return next; }
                set { next = value; }
            }
            public T Data       // T 作爲屬性的返回類型
            {
                get { return data; }
                set { data = value; }
            }
        }
        public GenericList()  // 構造函數
        {
            head = null;
        }
        public void AddHead(T t)    // T 作爲成員參數類型
        {   //新結點加入到當前列表的頭部,並且成爲head
            Node n = new Node(t);
            n.Next = head;
            head = n;
        }
        // Implementation of the iterator
        public System.Collections.Generic.IEnumerator<T> GetEnumerator()
        {   // 從頭到尾依次遍歷列表中每個節點
            Node current = head;
            while (current != null)
            {
                yield return current.Data;
                current = current.Next;
            }
        }
        // IEnumerable<T>繼承自IEnumerable,所以該類必須同時實現 
        // GetEnumerator的泛型和非泛型方法。通常非泛型方法可以通過
        // 直接調用泛型方法來簡化實現過程
        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }
    }

    public class SortedList<T> : GenericList<T> where T : System.IComparable<T>
    {
        // 一個簡單的、未經優化的排序算法:將列表元素從小到大排序
        public void BubbleSort()  // 冒泡排序
        {
            if (null == head || null == head.Next)
            {
                return;
            }
            bool swapped;

            do
            {
                Node previous = null;
                Node current = head;
                swapped = false;

                while (current.next != null)
                {   //  SortedList類在IEnumerable<T>上約束???
                    //  Because we need to call this method, the SortedList
                    //  class is constrained on IEnumerable<T>
                    if (current.Data.CompareTo(current.next.Data) > 0)
                    {
                        Node tmp = current.next;
                        current.next = current.next.next;
                        tmp.next = current;

                        if (previous == null)
                        {
                            head = tmp;
                        }
                        else
                        {
                            previous.next = tmp;
                        }
                        previous = tmp;
                        swapped = true;
                    }
                    else
                    {
                        previous = current;
                        current = current.next;
                    }
                }
            } while (swapped);
        }
    }

    // 定義一個簡單的類Person來實現IComparable<T> , 
    // 類Person本身作爲類型參數。
    public class Person : System.IComparable<Person>
    {
        string name;  // 姓名
        int age;     //  年齡

        public Person(string s, int i)
        {
            name = s;
            age = i;
        }

        // 對列表元素根據年齡排序.
        public int CompareTo(Person p)
        {
            return age - p.age;
        }

        public override string ToString()
        {
            return name + ":" + age;
        }

        // 實現Equals方法,判斷兩個人是否同齡.
        public bool Equals(Person p)
        {
            return (this.age == p.age);
        }
    }

    class TestSortedList
    {
        static void Main()
        {
            // 聲明和實例化一個新的泛型SortedList類.
            //  Person 作爲類型參數.
            SortedList<Person> list = new SortedList<Person>();

            // 初始化Person對象,對name和age賦初值.
            string[] names = new string[] 
        { 
            "劉一", 
            "陳二", 
            "張三", 
            "李四", 
            "王五", 
            "姚六", 
        };

            int[] ages = new int[] { 45, 19, 28, 23, 18, 79 };

            // 形成由name和age組成的列表.
            for (int x = 0; x < 6; x++)
            {
                list.AddHead(new Person(names[x], ages[x]));
            }

            // 打印無序列表.
            Console.WriteLine("初始(無序)列表爲:");
            foreach (Person p in list)
            {
                Console.Write(p.ToString() + "  ");
            }
            Console.WriteLine();

            // 列表排序.
            list.BubbleSort();

            // 打印有序列表.
            Console.WriteLine("\n按年齡排好序的列表爲:");
            foreach (Person p in list)
            {
                Console.Write(p.ToString() + "  ");
            }
            Console.ReadLine();
        }
    }
}

 

 

------- Windows Phone 7手機開發.Net培訓、期待與您交流! -------

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章