四:泛型

性能

装箱拆箱

        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();
        }
    }
}
View Code

一般来说,不用自己新建泛型类,只要使用.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; }
        }
    }
}
View Code

代码中如果实现的泛型接口不适用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; }
        }
    }
}
View Code

 

posted @ 2018-11-13 11:50  岚山夜话  阅读(90)  评论(0编辑  收藏  举报