泛型接口(C# 编程指南)

为泛型集合类或表示集合中的项的泛型类定义接口通常很有用处。 为避免对值类型执行装箱和取消装箱操作,最好对泛型类使用泛型接口,例如 IComparable<T>。 .NET 类库定义多个泛型接口,以便用于 System.Collections.Generic 命名空间中的集合类。 有关这些接口的详细信息,请参阅泛型接口

接口被指定为类型参数上的约束时,仅可使用实现接口的类型。 如下代码示例演示一个派生自 GenericList<T> 类的 SortedList<T> 类。 有关详细信息,请参阅泛型介绍SortedList<T> 添加约束 where T : IComparable<T>。 此约束可使 SortedList<T> 中的 BubbleSort 方法在列表元素上使用泛型 CompareTo 方法。 在此示例中,列表元素是一个实现 IComparable<Person> 的简单类 Person

//Type parameter T in angle brackets.
public class GenericList<T> : System.Collections.Generic.IEnumerable<T>
{
    protected Node head;
    protected Node current = null;

    // Nested class is also generic on T
    protected class Node
    {
        public Node next;
        private T data;  //T as private member datatype

        public Node(T t)  //T used in non-generic constructor
        {
            next = null;
            data = t;
        }

        public Node Next
        {
            get { return next; }
            set { next = value; }
        }

        public T Data  //T as return type of property
        {
            get { return data; }
            set { data = value; }
        }
    }

    public GenericList()  //constructor
    {
        head = null;
    }

    public void AddHead(T t)  //T as method parameter type
    {
        Node n = new Node(t);
        n.Next = head;
        head = n;
    }

    // Implementation of the iterator
    public System.Collections.Generic.IEnumerator<T> GetEnumerator()
    {
        Node current = head;
        while (current != null)
        {
            yield return current.Data;
            current = current.Next;
        }
    }

    // IEnumerable<T> inherits from IEnumerable, therefore this class
    // must implement both the generic and non-generic versions of
    // GetEnumerator. In most cases, the non-generic method can
    // simply call the generic method.
    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}

public class SortedList<T> : GenericList<T> where T : System.IComparable<T>
{
    // A simple, unoptimized sort algorithm that
    // orders list elements from lowest to highest:

    public void BubbleSort()
    {
        if (null == head || null == head.Next)
        {
            return;
        }
        bool swapped;

        do
        {
            Node previous = null;
            Node current = head;
            swapped = false;

            while (current.next != null)
            {
                //  Because we need to call this method, the SortedList
                //  class is constrained on IComparable<T>
                if (current.Data.CompareTo(current.next.Data) > 0)
                {
                    Node tmp = current.next;
                    current.next = current.next.next;
                    tmp.next = current;

                    if (previous == null)
                    {
                        head = tmp;
                    }
                    else
                    {
                        previous.next = tmp;
                    }
                    previous = tmp;
                    swapped = true;
                }
                else
                {
                    previous = current;
                    current = current.next;
                }
            }
        } while (swapped);
    }
}

// A simple class that implements IComparable<T> using itself as the
// type argument. This is a common design pattern in objects that
// are stored in generic lists.
public class Person : System.IComparable<Person>
{
    string name;
    int age;

    public Person(string s, int i)
    {
        name = s;
        age = i;
    }

    // This will cause list elements to be sorted on age values.
    public int CompareTo(Person p)
    {
        return age - p.age;
    }

    public override string ToString()
    {
        return name + ":" + age;
    }

    // Must implement Equals.
    public bool Equals(Person p)
    {
        return (this.age == p.age);
    }
}

public class Program
{
    public static void Main()
    {
        //Declare and instantiate a new generic SortedList class.
        //Person is the type argument.
        SortedList<Person> list = new SortedList<Person>();

        //Create name and age values to initialize Person objects.
        string[] names =
        [
            "Franscoise",
            "Bill",
            "Li",
            "Sandra",
            "Gunnar",
            "Alok",
            "Hiroyuki",
            "Maria",
            "Alessandro",
            "Raul"
        ];

        int[] ages = [45, 19, 28, 23, 18, 9, 108, 72, 30, 35];

        //Populate the list.
        for (int x = 0; x < 10; x++)
        {
            list.AddHead(new Person(names[x], ages[x]));
        }

        //Print out unsorted list.
        foreach (Person p in list)
        {
            System.Console.WriteLine(p.ToString());
        }
        System.Console.WriteLine("Done with unsorted list");

        //Sort the list.
        list.BubbleSort();

        //Print out sorted list.
        foreach (Person p in list)
        {
            System.Console.WriteLine(p.ToString());
        }
        System.Console.WriteLine("Done with sorted list");
    }
}

可将多个接口指定为单个类型上的约束,如下所示:

class Stack<T> where T : System.IComparable<T>, IEnumerable<T>
{
}

一个接口可定义多个类型参数,如下所示:

interface IDictionary<K, V>
{
}

适用于类的继承规则也适用于接口:

interface IMonth<T> { }

interface IJanuary : IMonth<int> { }  //No error
interface IFebruary<T> : IMonth<int> { }  //No error
interface IMarch<T> : IMonth<T> { }    //No error
                                       //interface IApril<T>  : IMonth<T, U> {}  //Error

如果泛型接口是协变的(即,仅使用自身的类型参数作为返回值),那么这些接口可继承自非泛型接口。 在 .NET 类库中,IEnumerable<T> 继承自 IEnumerable,因为 IEnumerable<T>GetEnumerator 的返回值和 Current 属性 Getter 中仅使用 T

具体类可实现封闭式构造接口,如下所示:

interface IBaseInterface<T> { }

class SampleClass : IBaseInterface<string> { }

只要类形参列表提供接口所需的所有实参,泛型类即可实现泛型接口或封闭式构造接口,如下所示:

interface IBaseInterface1<T> { }
interface IBaseInterface2<T, U> { }

class SampleClass1<T> : IBaseInterface1<T> { }          //No error
class SampleClass2<T> : IBaseInterface2<T, string> { }  //No error

控制方法重载的规则对泛型类、泛型结构或泛型接口内的方法一样。 有关详细信息,请参阅泛型方法

从 C# 11 开始,接口可以声明 static abstractstatic virtual 成员。 声明任一 static abstractstatic virtual 成员的接口几乎始终是泛型接口。 编译器必须在编译时解析对 static virtualstatic abstract 方法的调用。 接口中声明的 static virtualstatic abstract 方法没有类似于类中声明的 virtualabstract 方法的运行时调度机制。 相反,编译器使用编译时可用的类型信息。 这些成员通常是在泛型接口中声明的。 此外,声明 static virtualstatic abstract 方法的大多数接口都声明了其中一个类型参数必须实现已声明的接口。 然后,编译器使用提供的类型参数来解析声明成员的类型。

另请参阅