C# 泛型(4) 持续更新
泛型可以创建独立于被包含类型的类和方法。
C++模板与泛型相似。
泛型优点性能
System.Collections 和 System.Collections.Generic
名称空间泛型和非泛型集合类。
值类型存储在栈上,引用类型存储在堆上。C#类是引用类型,结构是值类型。
从值类型转化为引用类型称为装箱。方法需要把一个对象作为参数,传递了一个值类型,装箱操作就会自动进行。
装箱的值类型可以使用拆箱操作转换为值类型。在拆箱时,需要使用类型强制转换运算符。
如
ArrayList list = new ArrayList(); list.Add(4); int i1 = (int) list[0]; foreach (int i2 in list) { Console.WriteLine(i2); }
装箱和拆箱操作很容易使用,但性能损失比较大,遍历尤其如此。
List<T> 泛型类,不再进行装箱和拆箱操作
List<int> list = new List<int>(); list.Add(4); int i1 = list[0]; foreach (int i2 in list) { Console.WriteLine(i2); }
类型安全
ArrayList
ArrayList list = new ArrayList(); list.Add(4); list.Add("mystring"); list.Add(new ArrayList()); foreach (int i2 in list) { Console.WriteLine(i2); }
并不是所有元素都可以强制转换int。所以运行抛出异常。
错误应尽早发现。List<T> 泛型类就不会。
List<int> list = new List<int>(); list.Add(4); list.Add("mystring"); list.Add(new ArrayList());
直接在编译前,就报错。
二进制代码重用
List<Byte> list = new List<Byte>(); list.Add(4); List<string> list2 = new List<string>(); list2.Add("hello");
代码的扩展
引用类型在实例化的泛型类中只需要4个字节的内存地址(32位系统),就可以引用一个引用类型。值类型包含在实例化的泛型类的内存中,因为每个值类型对内存的要求不同,所以值类型实例化新类。
命名约定
- 泛型类行的名称用字母T作为前缀。
- 如果没有特殊要求,泛型类型允许用任意类替代,且只使用一个泛型类型,就可以用字符T作为泛型类型名称。
public class LinkedList<T> { }
- 如果泛型类型有特定要求(如实现一个接口或派生自基类),或者使用了两个或多个泛型类型。就应给泛型类型使用描述性的名称:
public delegate void EventHandler<TEventArgs>(object sender, TEventArgs e);
创建泛型类
一个非泛型的列表类。
public class LinkedListNode { public LinkedListNode(object value) { this.Value = value; } public object Value { get; private set; } public LinkedListNode Next { get; internal set; } public LinkedListNode Prev { get; internal set; } } public class LinkedList : IEnumerable { public LinkedListNode First { get; private set; } public LinkedListNode Last { get; private set; } public LinkedListNode AddLast(object node) { LinkedListNode newNode = new LinkedListNode(node); if (First == null) { First = newNode; Last = First; } else { LinkedListNode previous = Last; Last.Next = newNode; Last = newNode; Last.Prev = previous; } return newNode; } public IEnumerator GetEnumerator() { LinkedListNode current = First; while (current != null) { yield return current.Value; current = current.Next; } } }
yield语句创建一个枚举器的状态机。
LinkedList _linked = new LinkedList(); _linked.AddLast(2); _linked.AddLast(4); _linked.AddLast("6"); foreach (int i2 in _linked) { Console.WriteLine(i2); }
改成泛型类
using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Text; namespace ConsoleApplicationCShape { public class LinkedListNode<T> { public LinkedListNode(T value) { this.Value = value; } public T Value { get; private set; } public LinkedListNode<T> Next { get; internal set; } public LinkedListNode<T> Prev { get; internal set; } } public class LinkedList<T> : IEnumerable<T> { public LinkedListNode<T> First { get; private set; } public LinkedListNode<T> Last { get; private set; } public LinkedListNode<T> AddLast(T node) { var newNode = new LinkedListNode<T>(node); if (First == null) { First = newNode; Last = First; } else { Last.Next = newNode; Last = newNode; } return newNode; } public IEnumerator<T> GetEnumerator() { LinkedListNode<T> current = First; while (current != null) { yield return current.Value; current = current.Next; } } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } } class Program { static void Main(string[] args) { LinkedList<int> _linked = new LinkedList<int>(); _linked.AddLast(2); _linked.AddLast(4); _linked.AddLast(6); foreach (int i2 in _linked) { Console.WriteLine(i2); } Console.ReadLine(); } } }
泛型类的功能
泛型文档类管理
class DocumentManage<T> { private readonly Queue<T> documentQueue = new Queue<T>(); public void AddDocument(T doc) { lock (this) { documentQueue.Enqueue(doc); } } public bool IsDocumentAvilable { get { return documentQueue.Count > 0; } } }
默认值
null不能赋值给泛型类型。原因是 泛型类型也可以实例化为值类型,而null只能用于引用类型。
通过 default 将 null 赋值引用类型。
public T GetDocument() { T doc = default(T); lock (this) { doc = documentQueue.Dequeue(); } return doc; }
default关键字根据上下文有多种含义。 在switch表示默认值。在泛型中,用于泛型初始化 引用类型 赋值 null,值类型赋值0。
约束
public interface IDocument { string Title { get; set; } string Content { get; set; } } public class Document : IDocument { public Document() { } public Document(string title, string content) { this.Title = title; this.Content = content; } public string Title { get; set; } public string Content { get; set; } } class DocumentManage<TDocument> where TDocument: IDocument { private readonly Queue<TDocument> documentQueue = new Queue<TDocument>(); public void AddDocument(TDocument doc) { lock (this) { documentQueue.Enqueue(doc); } } public bool IsDocumentAvilable { get { return documentQueue.Count > 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); } } } class Program { static void Main(string[] args) { DocumentManage<Document> dm = new DocumentManage<Document>(); dm.AddDocument(new Document("Ttile A", "Content A")); dm.AddDocument(new Document("Ttile B", "Content B")); dm.DisplayAllDocuments(); while (dm.IsDocumentAvilable) { Document d = dm.GetDocument(); Console.WriteLine(d.Content); } } }
约束 TDocument 必须 实现 IDocment
where TDocument: IDocument
泛型其他的几种约束类型
value = where T:struct 对结构约束,类型T必须是值类型 where T:class 约束,类型T必须是引用类型 where T:IFoo 约束,类型T必须实现IFoo where T:Foo 约束,类型T必须派生自基类IFoo where T:new() 约束,类型T必须具有无参的构造函数 where T1:T2 约束,类型T1派生自泛型类型T2,称为裸类型约束
继承
实现泛型接口
public class LinkedList<T> : IEnumerable<T>
泛型派生泛型基类
public class Base<T> { } public class Derived<T> : Base<T> { }
指定基类类型
public class Derived<T> : Base<string> { }
public abstract class Calc<T> { public abstract T Add(T x, T y); public abstract T Sub(T x, T y); } public class IntCalc : Calc<int> { public override int Add(int x, int y) { return x + y; } public override int Sub(int x, int y) { return x - y; } }
静态成员
泛型类的静态成员,只能在类的静态成员类访问。
public class StaticDemo<T> { public static int x; } StaticDemo<string>.x = 10; StaticDemo<int>.x = 4; Console.WriteLine(StaticDemo<string>.x + " " + StaticDemo<int>.x);
泛型接口
使用泛型可以定义接口,在接口中定义的方法可以带泛型参数。
public interface IComparable<in T> { int CompareTo(T other); } public class Person : IComparable<Person> { private string LastName; public int CompareTo(Person other) { return this.LastName.CompareTo(other.LastName); } }
协变和抗变
泛型接口是不变的。通过协变和抗变为泛型接口和泛型委托添加了一个重要的扩展。协变和抗变指对参数和返回值的类型进行转换。
Rectangle 派生自 Shape
public class Shape { public double Width { get; set; } public double Height { get; set; } public override string ToString() { return String.Format("Width: {0}, Height: {1}", Width, Height); } } public class Rectangle : Shape { }
参数类型协变
public void Display(Shape o) { } Rectangle r = new Rectangle{Width = 5, Height = 3}; Display(r);
泛型接口的协变
out关键字标注,泛型接口是协变,意味着返回类型只能是T。
接口IIndex 与 类型 T 是协变,并从制度索引器返回这个类型。
public interface IIndex<out T> { T this[int index] { get; } int Count { get; } }
实现接口
public class RectangleCollection : IIndex<Rectangle> { private Rectangle[] data = new Rectangle[3] { new Rectangle { Height=2, Width=5 }, new Rectangle { Height=3, Width=7}, new Rectangle { Height=4.5, Width=2.9} }; private static RectangleCollection coll; public static RectangleCollection GetRectangles() { return coll ?? (coll = new RectangleCollection()); } public Rectangle this[int index] { get { if (index < 0 || index > data.Length) throw new ArgumentOutOfRangeException("index"); return data[index]; } } public int Count { get { return data.Length; } } }
coll ?? (coll = new RectangleCollection());
?? 合并运算符,如果 coll 为 null,将调用运算符的右侧。
static void Main(string[] args) { IIndex<Rectangle> rectangles = RectangleCollection.GetRectangles(); IIndex<Shape> shapes = rectangles; for (int i = 0; i < shapes.Count; i++) { Console.WriteLine(shapes[i]); } }
泛型接口抗变
in 关键标注 泛型接口是抗变的
public interface IDisplay<in T> { void Show(T item); }
使用Shape对象作为参数
public class ShapeDisplay : IDisplay<Shape> { public void Show(Shape s) { Console.WriteLine("{0} Width: {1}, Height: {2}", s.GetType().Name, s.Width, s.Height); } }
IDisplay<Shape> shapeDisplay = new ShapeDisplay(); IDisplay<Rectangle> rectangleDisplay = shapeDisplay; rectangleDisplay.Show(rectangles[0]);
在.NET中 参数类型是协变,返回值是抗变。
http://www.cnblogs.com/qionghua/archive/2012/08/02/2620486.html
泛型结构
public struct Nullable<T> where T:struct { public Nullable(T value) { this.hasValue = true; this.value = value; } private bool hasValue; public bool HasValue { get { return hasValue; } } private T value; public T Value { get { if (!hasValue) { throw new InvalidOperationException("no value"); } return value; } } public static explicit operator T(Nullable<T> value) { return value.Value; } public static implicit operator Nullable<T>(T value) { return new Nullable<T>(value); } public override string ToString() { if(!HasValue) return String.Empty; return this.value.ToString(); } }
Nullable<int> x; x = 4; x += 3; if (x.HasValue) { int y = x.Value; } x = null;
定义空类型值变量 使用 "?" 运算符
int? v2 = null; if (v2 == null) { Console.WriteLine("x is null"); }
static int? GetNullableType() { return null; } static void Main(string[] args) { int? x1 = GetNullableType(); int? x2 = GetNullableType(); int? x3 = x1 + x2; if (x3 == null) { Console.WriteLine("x3 is null"); } }
类型转换
int y1 = 4; int? x1 = y1; // error 不能将null赋值给,非可空类型 x1 = null; y1 = (int) x1; // 可以用合并运算符 提供默认值 y1 = x1 ?? 0;
泛型方法
static void Swap<T>(ref T x, ref T y) { T temp; temp = x; x = y; y = temp; } static void Main(string[] args) { int i = 4; int j = 5; Swap<int>(ref j, ref i); Console.WriteLine("i={0},j={1}",i,j); }
public class Account { public string Name { get; set; } public decimal Balance { get; private set; } public Account(string name, Decimal balance) { this.Name = name; this.Balance = balance; } } static void Main(string[] args) { List<Account> accounts = new List<Account>() { new Account("xxxx1",1500), new Account("xxxx2",2500), new Account("xxxx3",3500), new Account("xxxx4",4500) }; decimal sum = AccumulateSimple(accounts); Console.WriteLine("sum={0}",sum); } public static decimal AccumulateSimple(IEnumerable<Account> source) { decimal sum = 0; foreach (Account account in source) { sum += account.Balance; } return sum; }
IEnumerable 接口迭代集合元素。
约束的泛型方法
public class Account: IAccount { public string Name { get; set; } public decimal Balance { get; private set; } public Account(string name, Decimal balance) { this.Name = name; this.Balance = balance; } } public interface IAccount { decimal Balance { get; } string Name { get; } } public static decimal AccumulateSimple<TAccount>(IEnumerable<TAccount> source) where TAccount:IAccount { decimal sum = 0; foreach (TAccount account in source) { sum += account.Balance; } return sum; } List<Account> accounts = new List<Account>() { new Account("xxxx1",1500), new Account("xxxx2",2500), new Account("xxxx3",3500), new Account("xxxx4",4500) }; decimal sum = AccumulateSimple(accounts); Console.WriteLine("sum={0}",sum);
委托的泛型方法
public static T2 AccumulateSimple<T1, T2>(IEnumerable<T1> source,Func<T1,T2,T2> action) { T2 sum = default(T2); foreach (T1 item in source) { sum = action(item, sum); } return sum; } decimal total = AccumulateSimple<Account,decimal>(accounts,(item,sum) => sum += item.Balance); Console.WriteLine("sum={0}", total);
Lambda表达式 (item,sum) => sum += item.Balance