在建立泛型類時,需要一些其他C#關鍵字。例如,不能把null賦值給泛型型別,此時可以使用default關鍵字。如果泛型型別不需要object類的功能,但需要調用泛型類上的某些特定方法,就可以定義約束。
1.預設值
現在給DocumentManager<T>類添加一個GetDocument()方法
給類型T指定null,但是不能把null賦值給泛型型別
泛型型別也可以執行個體化為實值型別,但是null只能用於參考型別,解決這個問題可以使用default關鍵字
default關鍵字,將null賦予參考型別,將0賦予實值型別
using System;using System.Collections.Generic;using System.Linq;using System.Text;namespace Generics{ public class DocumentManager<T> { private readonly Queue<T> documentQueue = new Queue<T>(); public void AddDocument(T doc) { lock (this) { documentQueue.Enqueue(doc); } } public bool IsDocumentAvailable { get { return documentQueue.Count > 0; } } // 預設值 // 現在給DocumentManager<T>類添加一個GetDocument()方法 // 給類型T指定null,但是不能把null賦值給泛型型別 // 泛型型別也可以執行個體化為實值型別,但是null只能用於參考型別,解決這個問題可以使用default關鍵字 // default關鍵字,將null賦予參考型別,將0賦予實值型別 public T GetDocument() { T doc = default(T); lock (this) { doc = documentQueue.Dequeue(); } return doc; } } // 約束 // 如果泛型類需要調用泛型型別上的方法,就必須添加約束 public interface IDocument { string Title { get; set; } string Content { get; set; } } public class Document : IDocument { public string Title { get; set; } public string Content { get; set; } public Document() { } public Document(string title, string content) { this.Title= title; this.Content = content; } } class Program { static void Main(string[] args) { } }}
2.約束
如果泛型類需要調用泛型型別上的方法,就必須添加約束。
對於DocumentManager<T>,文檔的標題應在DisplayAllDocument()方法中顯示。
// 要使用DocumentManger<T>來顯示文檔,可以將類型T強制轉換為IDocument介面 public void DisplayAllDocuments() { foreach (T doc in documentQueue) { Console.WriteLine(((IDocument)doc).Title); } }
如果類型T沒有執行IDocument介面,這個類型轉換就會產生一個運行異常。最好的辦法就是給DocumentManger<TDocument>類定義一個約束:TDocument類型必須執行IDocument介面。
public class DocumentManager<TDocument> where TDcoument : IDocument
這樣上面的語句就可以這樣寫了
// 要使用DocumentManger<T>來顯示文檔,可以將類型T強制轉換為IDocument介面 public void DisplayAllDocuments() { foreach (TDocument doc in documentQueue) { Console.WriteLine(doc.Title); } }
下面是加了約束和帶有預設值特性的DocumentManager泛型類的完整代碼:
using System;using System.Collections.Generic;using System.Linq;using System.Text;namespace Generics{ public class DocumentManager<TDocument> where TDocument : IDocument { private readonly Queue<TDocument> documentQueue = new Queue<TDocument>(); public void AddDocument(TDocument doc) { lock (this) { documentQueue.Enqueue(doc); } } public bool IsDocumentAvailable { get { return documentQueue.Count > 0; } } // 預設值 // 現在給DocumentManager<T>類添加一個GetDocument()方法 // 給類型T指定null,但是不能把null賦值給泛型型別 // 泛型型別也可以執行個體化為實值型別,但是null只能用於參考型別,解決這個問題可以使用default關鍵字 // default關鍵字,將null賦予參考型別,將0賦予實值型別 public TDocument GetDocument() { TDocument doc = default(TDocument); lock (this) { doc = documentQueue.Dequeue(); } return doc; } public void DisplayAllDocuments() { foreach (TDocument doc in documentQueue) { Console.WriteLine(doc.Title); } } } // 約束 // 如果泛型類需要調用泛型型別上的方法,就必須添加約束 public interface IDocument { string Title { get; set; } string Content { get; set; } } public class Document : IDocument { public string Title { get; set; } public string Content { get; set; } public Document() { } public Document(string title, string content) { this.Title= title; this.Content = content; } } class Program { static void Main(string[] args) { DocumentManager<Document> dm = new DocumentManager<Document>(); dm.AddDocument(new Document("Title A", "Sample A")); dm.AddDocument(new Document("Title B", "Sample B")); dm.DisplayAllDocuments(); if (dm.IsDocumentAvailable) { Document d = dm.GetDocument(); Console.WriteLine(d.Content); } Console.ReadLine(); } }}
出來的結果有點問題: 這個需要後面看下,怎麼回事。為什麼Document B的內容顯示不了
Title A
Title B
Sample A
上面的例子是一個關於介面約束的例子。泛型還有幾種約束類型
where T : struct 使用結構約束 類型T必須是實值型別
where T : calss 類約束指定, 類型T必須是參考型別
where T : IFoo 指定類型必須執行介面IFoo
where T : Foo 指定類型必須派生於基類Foo
where T : new() 這是一個建構函式約束,指定類型T必須有一個預設建構函式
where T : U 這個約束也可以指定,類型T1派生於泛型型別T2。該約束也稱為裸類型約束
3.繼承
泛型型別可以執行泛型介面,也可以派生於一個類。下面是幾種泛型介面的繼承
public class Base<T>{}public class Derived<T> : Base<T>{}
也可以這樣:
public class Base<T>{}public class Derived<T> : Base<string>{}
衍生類別可以是泛型類也可以是非泛型類。例如下面的計算機
public abstrac class Clac<T>{ public abstrac T Add(T x, T y); public abstrac T Sub(T x, T y);}public class SimpleCalc : Calc<int>{ public override int Add(int x, int y) { return x + y; } public override int Sub(int x, int y) { return x - y; }}
這樣的程式看起來,非常乾淨不錯O(∩_∩)O~
4.靜態成員
泛型類的靜態成員需要特別關注。泛型類的靜態成員只能在類的一個執行個體中共用。看下面的例子就知道了
using System;using System.Collections.Generic;using System.Linq;using System.Text;namespace GenericStatic{ class Program { public class StaticDemo<T> { public static int x; } static void Main(string[] args) { StaticDemo<string>.x = 4; StaticDemo<int>.x = 5; Console.WriteLine(StaticDemo<string>.x); Console.ReadLine(); } }}
結果輸出肯定很清楚了
4