在创建泛型类时,需要一些其他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