الواجهات العامة (دليل البرمجة لـ #C)

من المفيد غالباً تعريف الواجهات إما لفئات المجموعات العامة أو لفئات عامة تمثل العناصر في المجموعة. تفضيل الفئات العامة استخدام واجهات عامة مثل IComparable<T> بدلاً من IComparable لتجنب عمليات التحويل إلى كائن وعمليات التحويل إلى نوع القيمة على أنواع القيمة. تعرّف مكتبة فئات .NET Framework عدة واجهات عامة للاستخدام مع فئات المجموعة في مساحة الاسم System.Collections.Generic.

عند استخدام الواجهة كقيد على معلمة النوع فيمكن استخدام الأنواع التي تقوم بتنفيذ هذه الواجهة فقط. مثال التعليمات البرمجية التالية يظهر الفئة SortedList<T> المشتقة من الفئة GenericList<T>. لمزيد من المعلومات، راجع مقدمة للأشياء العامة (دليل البرمجة لـ #C). SortedList<T> يضيف القيد where T : IComparable<T>. وهذا يمكّن الأسلوب BubbleSort في SortedList<T> لاستخدام الأسلوب العام CompareTo على عناصر القائمة. في هذا المثال، تكون عناصر القائمة فئة بسيطة Person تقوم بتطبيق 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 IEnumerable<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);
    }
}

class Program
{
    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 = new string[] 
        { 
            "Franscoise", 
            "Bill", 
            "Li", 
            "Sandra", 
            "Gunnar", 
            "Alok", 
            "Hiroyuki", 
            "Maria", 
            "Alessandro", 
            "Raul" 
        };

        int[] ages = new int[] { 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 Framework IEnumerable<T> ترث من IEnumerable لأن IEnumerable<T> تستخدم فقط T في قيمة الإرجاع لـ GetEnumerator وفي دالة استرجاع قيمة الخاصية Current.

يمكن للفئات الملموسة تنفيذ الواجهات المُنشأة المُغلقة كما يلي:

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# إرشادات).

راجع أيضًا:

المرجع

مقدمة للأشياء العامة (دليل البرمجة لـ #C)

الواجهة (مرجع C#)

المبادئ

دليل البرمجة لـ #C

موارد أخرى

generics في .NET Framework