四:泛型
性能
装箱拆箱
static void Main(string[] args) { var List = new ArrayList(); List.Add(44);//装箱 int i = (int)List[0];//拆箱 foreach(int item in List) { Console.WriteLine(item);//拆箱 } Console.ReadLine(); }
装箱拆箱是很平常的操作,但是问题是性能损失很大。
泛型的示例
static void Main(string[] args) { List<int> List = new List<int>(); List.Add(44);//没有装箱 int i = (int)List[0];//没有拆箱 foreach(int item in List) { Console.WriteLine(item);//没有拆箱 } Console.ReadLine(); }
使用泛型就没有装修和拆箱的性能损失。原因是代码编译后就已经指定List的类型是int,也就是值类型,不会再转换成Object类型。
类型安全
ArrayList添加的类型其实是Object。也就是说如果有类型的转换可能会有问题。
但是List<T>的定义就只能添加整型类型。
命名约定
一般泛型的名称以T作为前缀,如果有多个泛型类型,可以在T字面后面添加描述性名称
泛型类示例
using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace MyGeneric { class Program { static void Main(string[] args) { MyuLinkList<int> myList = new MyuLinkList<int>(); myList.AddList(1); myList.AddList(11); myList.AddList(111); foreach(var item in myList) { Console.WriteLine(item); } Console.ReadLine(); } } class MyuLinkListNode<T> { public MyuLinkListNode(T value) { this.Value = value; } public T Value { get; private set; } public MyuLinkListNode<T> Next { get; internal set; } public MyuLinkListNode<T> Prev { get; internal set; } } class MyuLinkList<T>:IEnumerable<T> { public MyuLinkListNode<T> First { get; internal set; } public MyuLinkListNode<T> Last { get; internal set; } public MyuLinkListNode<T> AddList(T node) { var newNode = new MyuLinkListNode<T>(node); if(First==null) { First = newNode; Last = First; } else { MyuLinkListNode<T> previous = Last; Last.Next = newNode; Last = newNode; Last.Prev = previous; } return newNode; } public IEnumerator<T> GetEnumerator() { MyuLinkListNode<T> current = First; while(current!=null) { yield return current.Value; current = current.Next; } } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } } }
一般来说,不用自己新建泛型类,只要使用.NET自带的泛型类就可以了。比如List<T>。
泛型的功能
泛型不能把null赋值给泛型类型。
主题有:默认值,约束,继承,静态成员
默认值
泛型的初始化不能赋值为null,而要使用default(T)
约束
public class DocumentMgt<T> where T:IDocument
where语句约束了泛型必须是IDocument或其实现
约束类型
- where T:struct:T必须是值类型
- where T:class : T必须是引用类型
- where T: IDoc:T必须实现接口IDoc
- where T: Doc :T必须派生自基类Doc
- where T: new() : 构造函数约束,T必须有一个默认的构造函数
- where T1:T2 T1必须派生自T2。该约束也称为裸类型约束
继承
public class Base<T> { } public class Derived<T>:Base<T> { }
静态成员
泛型类的静态成员只能在类的一个实例中共享
public class Base<T> { public static int x; } class Program { static void Main(string[] args) { Base<int>.x = 10; Base<string>.x = 20; Base<string>.x = 30; Console.WriteLine(Base<int>.x); Console.WriteLine(Base<string>.x); Console.ReadLine(); } }
结果是10,30。也就是或,同一种类型的泛型实现,算是一个实现类。不同的类型算是不同的实现类。
泛型接口
泛型可以定义接口,接口定义中可以使用泛型参数。.NET实现了很多泛型接口,如IEnumberable<out T>,IComparable<T>, ICollection<T>,IExtensibleObject<T>。
public class Person:IComparable<Person> { public Person(string name,int level) { this.Name = name; this.Level = level; } public string Name { get; set; } public int Level { get; set; } public int CompareTo(Person other) { return this.Level - other.Level; } } }
抗变/协变
抗变和协变是指参数或返回值的类型进行转换。
如ClassA,ClassB派生自ClassA。一个方法的Method(ClassA a)。那么调用该方法时只要是ClassA或其派生类即可。也就是说这时候参数可以是ClassB。这就称为协变。
如果一个方法的返回值是ClassB。因为ClassB派生自ClassA,那么就可以直接使用ClassA接受方法返回,这称为抗变。
泛型接口的协变
如果泛型类型使用的out关键字,那么泛型接口就是协变的。
using System; namespace GenieInterface { class Program { static void Main(string[] args) { RectangleCollection coll = new RectangleCollection(); IIndex<Rectangle> rec = RectangleCollection.GetRec(); IIndex<Shape> shapes = rec; Console.WriteLine(rec.Count); Console.WriteLine(coll[0]); Console.WriteLine(coll[2]); Console.ReadLine(); } } public interface IIndex<out T> { T this[int index] { get; } int Count { get; } } public class Shape { } public class Rectangle:Shape { public int H{get;set;} public int W{get;set;} public override string ToString() { return "Height:" + H.ToString() + ";Width:" + W.ToString(); } } public class RectangleCollection:IIndex<Rectangle> { private Rectangle[] data = new Rectangle[3] { new Rectangle{H=1,W=4}, new Rectangle{H=3,W=7}, new Rectangle{H=4,W=2} }; private static RectangleCollection coll; public static RectangleCollection GetRec() { 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; } } } }
代码中如果实现的泛型接口不适用out关键字,则转换成Shape时就会出错。
如果泛型类型使用in关键字标注,则泛型接口就是抗变的。
using System; namespace GenieInterface { class Program { static void Main(string[] args) { RectangleCollection coll = new RectangleCollection(); IIndex<Rectangle> rec = RectangleCollection.GetRec(); IIndex<Shape> shapes = rec; IDisplay<Shape> s1 = new ShapeDisplay(); IDisplay<Rectangle> r1 = s1; r1.Show(rec[0]); Console.WriteLine(rec.Count); Console.WriteLine(coll[0]); Console.WriteLine(coll[2]); Console.ReadLine(); } } public interface IDisplay<in T> { void Show(T item); } public class ShapeDisplay:IDisplay<Shape> { public void Show(Shape s) { Console.WriteLine("Shape Type {0}",s.GetType()); } } public interface IIndex<out T> { T this[int index] { get; } int Count { get; } } public class Shape { } public class Rectangle:Shape { public int H{get;set;} public int W{get;set;} public override string ToString() { return "Height:" + H.ToString() + ";Width:" + W.ToString(); } } public class RectangleCollection:IIndex<Rectangle> { private Rectangle[] data = new Rectangle[3] { new Rectangle{H=1,W=4}, new Rectangle{H=3,W=7}, new Rectangle{H=4,W=2} }; private static RectangleCollection coll; public static RectangleCollection GetRec() { 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; } } } }