C#系列学习笔记9:泛型

泛型

泛型,在初始化类、方法、接口等之前,延迟指定一个或多个类型,直到实际在程序中使用它的时候,这样可以降低运行时转换或装箱操作的成本或风险。有时候在封装代码的时候,我们不关心组件之间传递的是什么类型的数据,这时就可以用泛型来定义数据。System.Collections.Generic命名空间包含几个基于泛型的集合类。

在声明泛型类、结构的时候,我们可以通过使用 where 上下文关键字指定约束

public class Employee
{
    public Employee(string s, int i) => (Name, ID) = (s, i);
    public string Name { get; set; }
    public int ID { get; set; }
}

public class GenericList<T> where T : Employee  // 类型参数必须是指定的基类或派生自指定的基类
{
    private class Node
    {
        public Node(T t) => (Next, Data) = (null, t);

        public Node Next { get; set; }
        public T Data { get; set; }
    }

    private Node head;

    public void AddHead<T>(T t) where T struct   // T必须是不可为 null 的值类型
    {
        Node n = new Node(t) { Next = head };
        head = n;
    }

    public IEnumerator<T> GetEnumerator()
    {
        Node current = head;

        while (current != null)
        {
            yield return current.Data;
            current = current.Next;
        }
    }

}

常用泛型集合

List<T>. ArrayList的泛型等效类。位于 System.Collections.Generic
Dictionary<Tkey, Tvalue>. 与Hashtable等效的,称为泛型哈希表。位于 System.Collections.Generic
Queue<T>
Stack<T>

泛型是类的模板,类是实例的模板。使用泛型的类,在确定真正类型的时候才实例化;一个类,在实例化的实收才真正确定为对象。

协变和逆变

协变,简单的说可以认为是一个细节化程度高的类型赋值给细节化程度低的类型,向下兼容。逆变反之。

例如String继承自Object,因此:

// String -> Object 协变转换
string str = "test";  
object obj = str;  

// String -> Object 协变转换
IEnumerable<string> strings = new List<string>();  
IEnumerable<object> objects = strings;

static void SetObject(object obj) { }  
static object GetObject() { return null; }  

// Object -> String 逆变转换
Action<object> actObject = SetObject;  
Action<string> actString = GetObject;