قيود تشغيل معلمات نوع (إرشادات برمجة C#)

عندما تقوم بتعريف فئة عامة، يمكنك تطبيق القيود المفروضة على أنواع الأنواع التي يمكن استخدام رمز العميل لنوع الوسائط عند إنشاء مثيلات للفئة الخاصة بك. إذا كانت تعليمات العميل البرمجية تحاول إنشاء مثيل الفئة الخاصة بك باستخدام نوع غير مسموح بقيد، فإن النتيجة ستكون خطأ وقت الترجمة. تسمى هذه القيود بالقيود. يتم تعيين القيود باستخدام الكلمة الأساسية where حسب السياق. يسرد جدول التالي ستة أنواع للقيود:

قيد

الوصف

حيث T: بنية

يجب أن يكون النوع الأساسي نوع قيمة. أي نوع القيمة ماعدا Nullable يمكن أن يكون محدداً. لمزيد من المعلومات، راجع استخدام أنواع القيم الخالية ( ارشادات برمجة C# ).

حيث T: فئة

يجب أن تكون وسيطة النوع نوع مرجع؛ وهذا ينطبق أيضا على أي فئة أو واجهة، ومندوب أو نوع الصفيف.

حيث T: جديد

يجب أن يكون نوع وسيطة الدالة الإنشائية بدون معلمات عام. عند استخدامها معاً مع قيود أخرى, فإن القيد new() يجب تحديده عند النهاية.

حيث T: <اسم الفئة الأساسية>

يجب أن يكون نوع الوسيطة أو منحدرة من الفئة الأساسية المحددة.

حيث T: <اسم الواجهة>

يجب أن تكون وسيطة نوع أو تقوم بتنفيذ واجهة معينة. ويمكن تعيين قيود واجهة متعددة. يمكن أن يكون واجهة تقييد عام.

حيث T: U

يجب أن تكون وسيطة النوع المزودة لـ T أو مشتقة من وسيطة التزويد لـ U. يسمى هذا قيد نوع عاري.

لماذا تُستخدم القيود

إذا كنت ترغب بدراسة أي عنصر في قائمة عامة لتحديد ما إذا كان صالحاً أو لمقارنتة ببعض العناصر الأخرى، يجب أن يضمن المحول البرمجي أن المشغل أو أسلوب الاستدعاء سوف تكون مدعوماً بأي نوع وسيطة التي قد حددها تعليمات العميل 
البرمجية. هذا الضمان يتم الحصول عليه بتطبيق واحد أو أكثر من القيود لتعريف فئة عامة. على سبيل المثال، يخبر المحول البرمجي أن الكائنات الوحيدة من هذا النوع قيد الفئة الأساسية أو المستمدة من هذا النوع هو الذي سيتم استخدامه كنوع الوسائط. يمكن السماح للأساليب للأنواع التي لم يتم استدعاؤها في فئة عامة عندما يكون لدى المحول البرمجي هذا الضمان. يتم تعيين القيود باستخدام الكلمة الأساسية where حسب السياق. مثال التعليمات البرمجية التالية توضح وظائف ويمكننا أن نضيف إلى الفئة GenericList<T> (في مقدمة للأشياء العامة (دليل البرمجة لـ #C)) عن طريق تطبيق قيد فئة أساسية.

public class Employee
{
    private string name;
    private int id;

    public Employee(string s, int i)
    {
        name = s;
        id = i;
    }

    public string Name
    {
        get { return name; }
        set { name = value; }
    }

    public int ID
    {
        get { return id; }
        set { id = value; }
    }
}

public class GenericList<T> where T : Employee
{
    private class Node
    {
        private Node next;
        private T data;

        public Node(T t)
        {
            next = null;
            data = t;
        }

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

        public T Data
        {
            get { return data; }
            set { data = value; }
        }
    }

    private Node head;

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

    public void AddHead(T t)
    {
        Node n = new Node(t);
        n.Next = head;
        head = n;
    }

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

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

    public T FindFirstOccurrence(string s)
    {
        Node current = head;
        T t = null;

        while (current != null)
        {
            //The constraint enables access to the Name property.
            if (current.Data.Name == s)
            {
                t = current.Data;
                break;
            }
            else
            {
                current = current.Next;
            }
        }
        return t;
    }
}

يقوم القيد بتمكين فئة عامة لاستخدام الخاصية Employee.Name لأن كل العناصر من نوع T تضمن إما أن تكون كائنEmployee أو كائن يرث من Employee.

يمكن تطبيق القيود المتعددة على نفس نوع المعلمة والتي يمكن أن تكون القيود بأنفسها أنواع عامة، كما يلي:

class EmployeeList<T> where T : Employee, IEmployee, System.IComparable<T>, new()
{
    // ...
}

بواسطة تقييد معلمة نوع،فإنك تقوم بزيادة عدد العمليات المسموح بها، ويطلب الأسلوب التي تدعمها نوع تقييد وجميع الأنواع في التسلسل الهرمي لترثه. ولذلك، عند تصميمالفئات العامة أو الأساليب، إذا كنت ستقوم إجراء أي عملية تشغيل على أعضاء العامة خارج التعيينات البسيطة أو استدعاء أي من الأساليب غير معتمدة من قبلSystem.Object، سيكون عليك تطبيق قيود إلى نوع معلمة.

عند تطبيق قيد where T : class , قم بتجنب عوامل تشغيل == و != على معلمة النوع لأن هذه العوامل سوف تقوم باختبار لهوية المرجعية فقط، ليس من أجل المساواة القيمة. وهذا هو الحال حتى إذا كانت هذه العوامل تكون محملّة فوق طاقتها في نوع الذي يتم استخدامه كوسيطة. التعليمة البرمجية التالية توضح هذه النقطة؛ نقطة الإخراج غير صحيحة حتى وأن كانت الفئة String تحميل عامل التشغيل تحميل زائداً ==.

public static void OpTest<T>(T s, T t) where T : class
{
    System.Console.WriteLine(s == t);
}
static void Main()
{
    string s1 = "target";
    System.Text.StringBuilder sb = new System.Text.StringBuilder("target");
    string s2 = sb.ToString();
    OpTest<string>(s1, s2);
}

و السبب في هذا السلوك، هيأنه في وقت التحويل البرمجي يعرف المحوّل البرمجي فقط أنT هو نوع مرجع، ولذلك يجب استخدام العوامل الافتراضية التي تصلح لجميع أنواع المراجع. إذا كان يجب عليك اختبار مساواة القيمة, فإن الطريقة الموص بها هي أيضاً بتطبيق قيد where T : IComparable<T> و تنفيذ تلك الواجهة في الفئة التي سيتم استخدامها لإنشاء فئة عامة.

تقييد معلمات متعددة

يمكنك تطبيق قيود على معلمات متعددة، وقيود متعددة لمعلمة واحدة، كما هو موضح في المثال التالي:

class Base { }
class Test<T, U>
    where U : struct
    where T : Base, new() { }

معلمات نوع الغير المرتبطة

معلمات النوع التي لا تحتوي على قيود كما T في الفئة العامة SampleClass<T>{}، تسمى معلمات نوع غير مرتبطة. نوع المعلمات الغير مرتبطة لها القواعد التالية:

  • عوامل التشغيل != و == لا يمكن استخدامها لأنه لا يوجد هناك أي ضمان بأن وسيطة نوع الخرسانة ستعتمد هذه العوامل.

  • وهي يمكن تحويلها من و إلى System.Object أو تحويله بوضوح إلى أي نوع واجهة.

  • يمكنك مقارنتها بـ قيمة خالية. وإذا كانت المقارنة مع معلمة غير المرتبطة null، سترجع المقارنة دائماً خطأ إذا كانت وسيطة النوع هي نوع القيمة.

قيود نوع عاري

عندما يتم استخدام معلمة نوع عام كقيد، فإنه يسمى قيد نوع عاري. تكون قيود النوع العارية مفيدة عندما يكون دالة عضو مع معلمة النوع الخاص به تقيد هذه المعلمة لمعلمة نوع من النوع الذي يتضمن، كما هو موضح في المثال التالي:

class List<T>
{
    void Add<U>(List<U> items) where U : T {/*...*/}
}

في المثال السابق، يكون T قيد النوعالعاري في السياق للأسلوب Add , و نوع معلمة غير مرتبطة في سياق فئة List.

يمكن أيضاً استخدام قيود النوع العاري في ملفات تعريف الفئة العامة. لاحظ أن قيد النوعالعاري يجب أيضاً أن تكون معّرفة بالأقواس المعقوفة مع أية معلمات أخرى في نوع:

//naked type constraint
public class SampleClass<T, U, V> where T : V { }

فائدة قيود النوع العاري مع فئات عامة محدودة للغاية لأن المحوّل البرمجي يفرض أي شيء عن قيد النوع العاري إلا أن ذلك نابع من System.Object. استخدام قيود النوع العاري المفروضة على فئات عامة في السيناريوهات التي تريد فرض علاقة وراثة بين معلمتين نوع.

راجع أيضًا:

المرجع

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

الفئات العامة ( ارشادات برمجة C#)

قيد جديد (C# مرجع)

System.Collections.Generic

المبادئ

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