C#泛型編程


1.泛型的概念

    C#中的泛型與C++中的模板類似,泛型是實例化過程中提供的類型或類建立的。泛型並不限於類,還可以創建泛型接口、泛型方法,甚至泛型委托。這將極大提高代碼的靈活性,正確使用泛型可以顯著縮短開發時間。與C++不同的是,C#中所有操作都是在運行期間進行的。

2.使用泛型

  •  可空類型

       值類型必須包含一個值,它們可以在聲明之后,賦值之前,在未賦值狀態下存在,但不能以任何方式使用,而引用類型可以為null。有時讓值類型為空是很有用的,泛型提供了使用System.Nullable<T>使值類型為空的一種方式。如下代碼:

    private Nullable<int> _nullableInt;

    則可以為_nullableInt賦為null,如下:

    _nullableInt = null;

可空類型非常有用,以致於C#中加入了語法:

int? _nullableIntSecond;

  • System.Collections.Generic 名稱空間

    這個名稱空間用於處理集合的泛型類型,使用的非常頻繁。將以List<T>和Dictionary<K,V>為例介紹這些類,以及和它們配合使用的接口和方法。

        1.List<T>

        List<T>泛型集合類更加快捷,更易於使用,創建T類型對象的集合需要一下方法:

        List<string> _myCollection = new List<string>();

        將創建T為String的List集合。

        可以在代碼中去查看List<T>所支持的方法,這里不再贅述。

       2.對泛型列表進行排序和搜索

        對泛型列表進行排序和搜索與和其它列表進行排序和搜索是一樣的,如下為實例代碼:

            

public class NumberCollection : List<int>
    {
        public NumberCollection(IEnumerable<int> initialNums)
        {
            foreach (var num in initialNums)
            {
                Add(num);
            }
        }

        public NumberCollection()
        {
            for (int i=0;i < 10;++i)
            {
                Add(i);
            }
        }

        public void Print()
        {
            foreach (var num in this)
            {
                Console.WriteLine(num);
            }

            Console.Read();
        }
    }
public static class NumberCollectionDelegate
    {
        public static int Compare(int i,int j)
        {
            if (i > j)
            {
                return -1;
            }
            else if (i < j)
            {
                return 1;
            }
            return 0;
        }

        public static bool Find(int i)
        {
            if (i %2 == 0)
            {
                return true;
            }

            return false;
        }

         public static Comparison<int> CopmareDelegate = new Comparison<int>(Compare);

 
         

         public static Predicate<int> Predicate = new Predicate<int>(Find);


    }
var numColleciton = new NumberCollection();
numColleciton.Print();
numColleciton.Sort(NumberCollectionDelegate.CopmareDelegate);
numColleciton.Print();
var newNumCollection = new NumberCollection(numColleciton.FindAll(NumberCollectionDelegate.Predicate));
newNumCollection.Print();
Console.ReadLine();

如上代碼,首先定義了NumberCollection繼承自List<int>,定義了Print方法用來輸出集合中所有的值,類NumberCollectionDelegate中定義了Compare方法,以及Find方法,Compare方法用於對集合進行降序排序(為什么這么寫是降序排序請關注另一篇文章),Find方法用於選擇集合中為偶數的值。在實例的使用中,首先對集合中的元素進行了降序排序,后選擇集合中為偶數的值組成新的集合,當然上述比較與查找用法可以簡化為以下用法:

numColleciton.Sort(NumberCollectionDelegate.Compare);

var newNumCollection = new NumberCollection(numColleciton.FindAll(NumberCollectionDelegate.Find));

這樣就不需要顯示引用Comparison<int>類型了,但是在使用時仍然會隱式創建Comparison<int>實例,對於比較也是同樣的。在許多情況下,都可以使用方法組以這種方式隱式的創建委托,使代碼變的更容易讀取。

3.Dictionary<K,V>

    這個類型可以定義鍵值對的集合,這個類型需要實例化兩個類型,分別用於鍵和值,以表示集合中的各個項。可以使用強類型化的Add方法添加鍵值對,如下。

Dictionary<string, int> stringIntDictionary = new Dictionary<string, int>();
stringIntDictionary.Add("Tom", 1);
stringIntDictionary.Add("Lucy", 2);
stringIntDictionary.Add("Lily", 3);

可以直接訪問Dicitionary中的keys和Values屬性值:

foreach (var key in stringIntDictionary.Keys)
{
Console.WriteLine(key);
}

foreach (var value in stringIntDictionary.Values)
{
Console.WriteLine(value);
}

同樣可以迭代集合中每一項,如下:

foreach (KeyValuePair<string,int> item in stringIntDictionary)
{
Console.WriteLine("{0} = {1}", item.Key, item.Value);
}

對於Dictionary<K,V>需要注意的一點是,每個項的鍵都必須式唯一的。如果要添加的項與已存在的項的鍵值相同,則會拋出異常。

3.定義泛型

    1.定義泛型類

         要創建泛型類,只需在類定義中包括尖括號:

class MyGenericClass<T>
{
}

其中T可以是任意標識符,只需要遵循C#命名規則即可,但一般只使用T。

泛型類可以在其定義中包含任意多個類型,它們用逗號分開,例如:

class MyGenericClass<T1,T2,T3>
{
}

定義了這些類型之后,就可以在類定義中像使用其它類型那樣使用它們,如下

class MyGenericClass<T1,T2,T3>
    {
        private T1 _object;

        public MyGenericClass(T1 item)
        {
              _object = item;
        }

        public T1 InnerT1Object
        {
            get
            {
                return _object;
            }
        }
    }

注意不能假定使用了什么類型,例如:

_object = new T1();

因為此刻不知道T1是什么,也就不能使用它的構造函數,可能T1就沒有構造函數,或者沒有可公共訪問的構造函數。因此要對泛型進行實際的操作需要更多了解其使用的類型。

  • default關鍵字

    要確定用於創建泛型類型的實例,需要了解一個最基本的情況,它是引用類型還是值類型,若不了解這個情況就不能直接對變量賦予null值。此時default關鍵字就派上了用場:

_object = default(T1);

如果_object是引用類型就給引用類型賦為null值,如果為值類型就給它賦為默認值。對於數字類型默認值為0,對於結構,按照相同的規則對它們進行賦值。default關鍵字允許對必須使用的類型進行更多的操作,為了進行更多的操作,必須對使用的類型進行更多的約束。

  • 約束類型

    前面使用的類型稱為無綁定類型,因為沒有對它們進行任何約束,而通過約束可以限定用於泛型的類型。在類定義中,可以使用where關鍵字,來限定用於泛型的類型,如下:

class MyGenericClass<T1,T2,T3> where T1 : constraint

其中constraint定義了約束,可以用這種能方式定義很多約束,每個約束之間用逗號分開。還可以使用多個where語句,定義泛型類型需要的任意類型或所有類型上的約束,約束必須出現在類型說明符的后面。

  • 從泛型類中繼承

    類可以從泛型中繼承,如

class Farm<T> : IEnumerable<T> where T : Animal
{
}

如上代碼Farm<T>是一個接口類型,同樣對於T的約束也會在IEnumerable中使用的T上添加一個額外的約束,這可以用於限制用於約束的類型,但是需要遵守一些規則。

    首先,如果某個類型所繼承的基類型中受到了約束,該類型就不能接觸約束,即類型T在基類中使用時所受到的約束,必須擴展到子類中,至少於基類的約束相同。

  • 泛型運算符

    泛型類也支持運算符的重寫。

  • 泛型結構

    結構與類相同,只是有一些細微的差別,而且結構是值類型,不是引用類型,所以可以創建泛型結構,如:

struct MyGenericStruct<T1, T2>
{
}

2.定義泛型接口

定義泛型接口於定義泛型類所用的技術相同,例如:

  interface IGeneric<T> where T : Object
    {
        void Sum(T x, T y);
    }

3.定義泛型方法

    可以使用泛型方法以達到泛型方法的更一般形式,在泛型方法中,參數類型或返回類型由泛型類型參數所決定。

,例如:

  T GetDefault<T>()
           {
               return default(T);
           }

可以使用非泛型類,實現泛型方法:

   public class Defaulter
    {
        public T GetDefault<T>()
        {
            return default(T);
        }
    }

 如果類是泛型的,那么需要為類中的泛型方法提供不同的標示符。如下代碼會提示泛型方法:

  public class Defaulter<T>
    {
        public T GetDefault<T>()
        {
            return default(T);
        }
    }

會提示內部泛型參數與外部泛型參數相同,此時應該更改泛型標示符。

 

本篇內容參考C#入門經典。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM