Share via


Algemene interfaces (C#-programmeerhandleiding)

Het is vaak handig om interfaces te definiëren voor algemene verzamelingsklassen of voor de algemene klassen die items in de verzameling vertegenwoordigen. Om boksen en het uitpakken van bewerkingen voor waardetypen te voorkomen, is het beter om algemene interfaces, zoalsIComparable<T>, te gebruiken voor algemene klassen. De .NET-klassebibliotheek definieert verschillende algemene interfaces voor gebruik met de verzamelingsklassen in de System.Collections.Generic naamruimte. Zie Algemene interfaces voor meer informatie over deze interfaces.

Wanneer een interface is opgegeven als een beperking voor een typeparameter, kunnen alleen typen worden gebruikt die de interface implementeren. In het volgende codevoorbeeld ziet u een SortedList<T> klasse die is afgeleid van de GenericList<T> klasse. Zie Inleiding tot generics voor meer informatie. SortedList<T> voegt de beperking where T : IComparable<T>toe. Met deze beperking kan de methode SortedList<T> de BubbleSort algemene CompareTo methode gebruiken voor lijstelementen. In dit voorbeeld zijn lijstelementen een eenvoudige klasse die Person wordt geïmplementeerd IComparable<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");
    }
}

Meerdere interfaces kunnen als volgt worden opgegeven als beperkingen voor één type:

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

Een interface kan als volgt meer dan één typeparameter definiëren:

interface IDictionary<K, V>
{
}

De overnameregels die van toepassing zijn op klassen, zijn ook van toepassing op interfaces:

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

Algemene interfaces kunnen overnemen van niet-algemene interfaces als de algemene interface covariant is, wat betekent dat deze alleen de typeparameter als retourwaarde gebruikt. In de .NET-klassebibliotheek IEnumerable<T> wordt overgenomen van IEnumerable waaruit IEnumerable<T> alleen wordt gebruikt T in de retourwaarde van GetEnumerator en in de Current eigenschap getter.

Concrete klassen kunnen als volgt gesloten geconstrueerde interfaces implementeren:

interface IBaseInterface<T> { }

class SampleClass : IBaseInterface<string> { }

Algemene klassen kunnen algemene interfaces of gesloten samengestelde interfaces implementeren zolang de lijst met klasseparameters alle argumenten levert die voor de interface zijn vereist, als volgt:

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

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

De regels voor het overbelasten van methoden zijn hetzelfde voor methoden binnen algemene klassen, algemene structs of algemene interfaces. Zie Algemene methoden voor meer informatie.

Vanaf C# 11 kunnen interfaces declareren of static virtual leden.static abstract Interfaces die een van static abstract beide of static virtual leden declareren, zijn vrijwel altijd algemene interfaces. De compiler moet aanroepen en static virtualstatic abstract methoden tijdens het compileren oplossen. static virtual en static abstract methoden die zijn gedeclareerd in interfaces hebben geen runtime-verzendmechanisme dat vergelijkbaar is met virtual of abstract methoden die zijn gedeclareerd in klassen. In plaats daarvan gebruikt de compiler typegegevens die beschikbaar zijn tijdens het compileren. Deze leden worden doorgaans gedeclareerd in algemene interfaces. Bovendien moeten de meeste interfaces die declareren of methoden declareren static virtual dat een van de typeparameters de gedeclareerde interface moet implementeren.static abstract De compiler gebruikt vervolgens de opgegeven typeargumenten om het type van het gedeclareerde lid op te lossen.

Zie ook