مشاركة عبر


التباين المشترك و التباين المضاد في العناصر العامة

توفر معلمات النوع العام المتباينة تبايناً مشتركاً و المتباينة تبايناً عكسياً مرونة أكبر في تعيين واستخدام الأنواع العامة. على سبيل المثال، معلمات النوع المتباينة تبايناً مشتركاً تمكنك من عمل تعيينات تبدو أقرب كثيراً للتعدد العادي. افترض أن لديك فئة أساسية و فئة مشتقة باسم Base و Derived. التعدد يمكنك من تعيين مثيل من Derived لمتغير من نوع Base. وبشكل مماثل ، لأن معلمة النوع للواجهة IEnumerable<T> متباينة تبايناً مشتركاً ، يمكنك تعيين مثيل من IEnumerable<Derived> ( IEnumerable(Of Derived) في Visual Basic) إلى متغير من النوع IEnumerable<Base> ، كما هو موضح في التعليمات البرمجية التالية.

Dim d As IEnumerable(Of Derived) = New List(Of Derived)
Dim b As IEnumerable(Of Base) = d
IEnumerable<Derived> d = new List<Derived>();
IEnumerable<Base> b = d;

الفئة List<T> تنفذ الواجهة IEnumerable<T> , لذا List<Derived> (List(Of Derived) في Visual Basic) تنفذ IEnumerable<Derived>. معلمة النوع المتباينة تبايناً مشتركاً تقوم بالباقي.

يبدو التباين المشترك طبيعي جداً لأنه يشبه التعدد. ومن ناحية أخرى، يبدو التباين العكسي غير معقول. المثال التالي ينشئ مفوض من النوع Action<Base> ( Action(Of Base) في Visual Basic) ، ثم يعيّن ذلك المفوض لمتغير من النوع Action<Derived>.

Dim b As Action(Of Base) = Sub(target As Base) 
                               Console.WriteLine(target.GetType().Name)
                           End Sub
Dim d As Action(Of Derived) = b
d(New Derived())
Action<Base> b = (target) => { Console.WriteLine(target.GetType().Name); };
Action<Derived> d = b;
d(new Derived());

هذا يبدو رجعي , و لكنها تعليمات برمجية آمنة للنوع تُحَوّل برمجيًا و تعمل. التعبير لامدا يطابق المفوض المُعَيّن إليه, لذا يُعَرِف أسلوب يأخذ معلمة واحدة من النوع Base و هذا الأسلوب ليس له قيمة مرجعة. يمكن تعيين المفوض الناتج إلى متغير من النوع Action<Derived> لأن معلمة النوع T للمفوض Action<T> متباينة تبايناً عكسياً. التعليمات البرمجية آمنة للنوع لأن T يحدد نوع معلمة. عندما استدعاء المفوض من النوع Action<Base> كما لو كان مفوض من النوع Action<Derived> ، يجب أن تكون الوسيطة الخاصة بها من النوع Derived. هذه الوسيطة يمكن دوماً تمريرها بأمان إلى الأسلوب الضمني , لأن معلمة الأسلوب من النوع Base.

بشكل عام ، معلمة النوع المتباينة تبايناً مشتركاً يمكن استخدامها لنوع الإرجاع لمفوض , و معلمة النوع المتباينة تبايناً عكسياً يمكن استخدامها لأنواع المعلمات. للواجهة , معلمات النوع المتباينة تبايناً مشتركاً يمكن استخدامها كأنواع الإرجاع لأساليب الواجهة , و معلمات النوع المتباينة تبايناً عكسياً يمكن استخدامها لأنواع معلمات أساليب الواجهة.

التباين المشترك و التباين المضاد يشار إليها معاً بالتباين . معلمة النوع العام التي لم يتم وضع علامة عليها ك متباينة تبايناً مشتركاً أو متباينة تبايناً عكسياً يشار إليها كـثابتة . ملخص مختصر للحقائق حول التباين في وقت تشغيل اللغة العامة:

  • في .NET Framework الإصدار 4 ، معلمات النوع المتباينة مقيدة إلى واجهة عامة و مفوض عام.

  • نوع الواجهة العامة أو المفوض العام يمكن أن يكون له كلا من معلمات النوع المتباينة تبايناً مشتركاً و المتباينة تبايناً عكسياً.

  • التباين يطبق فقط على الأنواع المرجعية ; إذا قمت بتحديد نوع قيمة لمعلمة نوع متباينة , معلمة النوع تلك ثابتة للنوع المنشئ الناتج.

  • التباين لا يُطَبَق لتركيبة المفوض. وهذا يعني , لو لديك مُفوضيّن من النوعين Action<Derived> و Action<Base> (Action(Of Derived) و Action(Of Base) في Visual Basic) ، لا يمكنك ضم المفوض الثاني مع الأول على الرغم من أن النتيجة ستكون آمنة للنوع. التباين يسمح للمفوض الثاني بأن يتم تعيينه لمتغير من النوع Action<Derived> ، و لكن يمكن ضم المفوضات فقط إذا تطابقت أنواعها تماماً.

الأقسام الفرعية التالية تصف معلمات النوع المتباينة تبايناً مشتركاً و المتباينة تبايناً عكسياً بالتفصيل:

  • الواجهات العامة مع معلمات النوع المتباينة تبايناً مشتركاً

  • الواجهات العامة مع معلمات النوع المتباينة تبايناً عكسياً

  • المفوضات العامة مع معلمات النوع المتباينة

  • تعريف واجهات و مفوضات عامة متباينة

  • قائمة بأنواع الواجهات و المفوضات العامة المتباينة

الواجهات العامة مع معلمات النوع المتباينة تبايناً مشتركاً

بدءاً بالـ .NET Framework 4 ، عدة من الواجهات العامة لديها معلمات نوع متباينة تبايناً مشتركاً ; على سبيل المثال: IEnumerable<T> ، و IEnumerator<T> ، و IQueryable<T> ، وIGrouping<TKey, TElement>. كل معلمات النوع لتلك الواجهات متباينة تبايناً مشتركاً ، لذا يتم استخدام معلمات النوع فقط لأنواع الإرجاع للأعضاء. 

ملاحظةملاحظة

الواجهة Converter<TInput, TOutput> لديها كلا من معلمات النوع المتباينة تبايناً مشتركاً و المتباينة تبايناً عكسياً.معلمات النوع المتباينة تبايناً عكسياً يتم استخدامها فقط من أجل أنواع معلمات أعضاءها.

يوضح المثال التالي معلمات النوع المتباينة تبايناً مشتركاً . يعرف المثال نوعين: Baseأسلوب ثابت باسمPrintBasesالتي تأخذIEnumerable<Base>(IEnumerable(Of Base)في Visual أساسى) و طباعة العناصر. Derivedيرث منBase. المثال، يتم إنشاء List<Derived>(List(Of Derived)في Visual أساسى) فارغ و يوضح أنه يمكن تمرير هذا النوع إلى PrintBasesو المعينة إلى متغير من نوع IEnumerable<Base>دون النوع. List<T>يطبقIEnumerable<T>، الذي يحتوي على معلمة نوع متغير طردي مفرد. معلمة النوع المتباينة تبايناً مشتركاً هي السبب لإمكانية إستخدام مثيل من IEnumerable<Derived> بدلاً من IEnumerable<Base>.

Imports System.Collections.Generic

Class Base
    Public Shared Sub PrintBases(ByVal bases As IEnumerable(Of Base))
        For Each b As Base In bases
            Console.WriteLine(b)
        Next
    End Sub    
End Class

Class Derived
    Inherits Base

    Shared Sub Main()
        Dim dlist As New List(Of Derived)()

        Derived.PrintBases(dlist)
        Dim bIEnum As IEnumerable(Of Base) = dlist
    End Sub
End Class
using System;
using System.Collections.Generic;

class Base
{
    public static void PrintBases(IEnumerable<Base> bases)
    {
        foreach(Base b in bases)
        {
            Console.WriteLine(b);
        }
    }
}

class Derived : Base
{
    public static void Main()
    {
        List<Derived> dlist = new List<Derived>();

        Derived.PrintBases(dlist);
        IEnumerable<Base> bIEnum = dlist;
    }
}

العودة إلى الأعلى

الواجهات العامة مع معلمات النوع المتباينة تبايناً عكسياً

بدءاً بالـ .NET Framework 4 ، عدة من الواجهات العامة لديها معلمات نوع متباينة تبايناً عكسياً ; على سبيل المثال: IComparer<T> ، IComparable<T> و IEqualityComparer<T> هذه الواجهات لديها معلمات نوع متباينة تبايناً عكسياً فقط , لذا يتم استخدام معلمات النوع فقط لأنواع المعلمات في أعضاء الواجهات. 

ملاحظةملاحظة

الواجهة Converter<TInput, TOutput> لديها كلا من معلمات النوع المتباينة تبايناً مشتركاً و المتباينة تبايناً عكسياً.معلمات النوع المتباينة تبايناً مشتركاً يتم استخدامها فقط من أجل أنواع إرجاع أعضاءها.

يوضح المثال التالي معلمات النوع المتباينة تبايناً عكسياً . يُعَرِف المثال فئة Account التي لها خاصيتين: رقم تعريف حساب و رقم تعريف عميل. CreditCardAccountيرثAccount. مع افتراض أن العميل يمكن أن يكون لديه عدة حسابات ، قد يكون مفيداً أن تكون قادراً على الحصول على قائمة بالعملاء بدون تكرار. لفعل ذلك، المثال يُعَرِف فئة CustomerIdentityComparer التي تنفذ IEqualityComparer<Account> (IEqualityComparer(Of Account) في Visual Basic). معلمة النوع للواجهة العامة IEqualityComparer<T> متباينة تبايناً مشتركاً. يتيح لك هذا تمرير نوع أقل إشتقاقاً لمقارن (Account) عندما تستدعي التعليمات البرمجية مقارن لنوع أكثر إشتقاقاً(CreditCardAccount). عندما يستدعي المثال في الأسلوب الملحق Distinct على مصفوفة من الكائنات CreditCardAccount , كل من CreditCardAccount يتم تمريره إلى الأسلوب Equals من الأسلوب IEqualityComparer<Account>. نوع معلمة الأسلوب أقل إشتقاقاً من النوع الذي يتم تمريره , لذا الاستدعاء آمن للنوع.

Class Account
    Public Property AccountID As Long
    Public Property CustomerID As Long
End Class

Class CreditCardAccount
    Inherits Account
End Class

Class CustomerIdentityComparer
    Implements System.Collections.Generic.IEqualityComparer(Of Account)

    Private Function CustomerEquals(ByVal a As Account, ByVal b As Account) As Boolean _
            Implements System.Collections.Generic.IEqualityComparer(Of Account).Equals
        Return If(a Is b, True, If(a Is Nothing OrElse b Is Nothing, False,
                  a.CustomerID = b.CustomerID))
    End Function
    Private Function GetCustomerHashCode(ByVal a As Account) As Integer _
            Implements System.Collections.Generic.IEqualityComparer(Of Account).GetHashCode
        Return If(a Is Nothing, 0, a.CustomerID.GetHashCode())
    End Function
End Class

Class Program
    Shared Sub Main()
        Dim creditCards(3) As CreditCardAccount

        Dim noduplicates = creditCards.Distinct(New CustomerIdentityComparer())
    End Sub
End Class
using System;
using System.Linq;
using System.Collections.Generic;

class Account
{
    public long AccountID { get; set; }
    public long CustomerID { get; set; }
}

class CreditCardAccount : Account {}

class CustomerIdentityComparer : IEqualityComparer<Account>
{
    bool IEqualityComparer<Account>.Equals(Account a, Account b)
    {
        return a == b ? true : a == null || b == null ? false : a.CustomerID == b.CustomerID;
    }
    int IEqualityComparer<Account>.GetHashCode(Account a)
    {
        return a == null ? 0 : a.CustomerID.GetHashCode();
    }
}

class Program
{
    static void Main()
    {
        CreditCardAccount[] creditCards = new CreditCardAccount[3]; 

        var noduplicates = creditCards.Distinct(new CustomerIdentityComparer());
    }
}

العودة إلى الأعلى

المفوضات العامة مع معلمات النوع المتباينة

في الـ .NET Framework 4 ، تظهر المفوضات العامة Func , مثل Func<T, TResult> ، لديها أنواع إرجاع متباينة تبايناً مشتركاً و أنواع معلمات متباينة تبايناً عكسياً. المفوضات العامة Action , مثل Action<T1, T2> ، لديها أنواع معلمات متباينة تبايناً عكسياً. هذا يعني أن المفوضات يمكن تعيينها إلى متغيرات لها أنواع معلمات أكثر اشتقاقاُ و (في حالة المفوضات العامة Func) أنواع إرجاع أقل اشتقاقاً.

ملاحظةملاحظة

معلمة النوع العام الأخيرة للمفوضات العامة Func تحدد نوع قيمة الإرجاع في توقيع المفوض.إنها متباينة تبايناً مشتركاً (الكلمة المفتاحية out) ، بينما معلمات النوع العام الأخرى متباينة تبايناً عكسياً (الكلمة المفتاحية in).

التعليمات البرمجية التالية توضح ذلك. يُعَرِف الجزء الأول من التعليمات البرمجية فئة باسم Base ، الفئة المسماة Derived التي ترث Base ، و فئة أخرى مع أسلوب static (Shared في Visual Basic) المسمى MyMethod. الأسلوب يأخذ مثيل من Base و يرجع مثيل من Derived. (إذا كانت الوسيطة مثيل من Derived ، MyMethod ترجعها ; إذا كانت الوسيطة مثيل من Base ، MyMethod ترجع مثيل جديد من Derived.) في Main() ، المثال ينشئ مثيل من Func<Base, Derived> ( Func(Of Base, Derived) في Visual Basic) الذي يمثل MyMethod ، ويخزنها في المتغير f1.

Public Class Base 
End Class
Public Class Derived
    Inherits Base
End Class

Public Class Program
    Public Shared Function MyMethod(ByVal b As Base) As Derived 
        Return If(TypeOf b Is Derived, b, New Derived())
    End Function

    Shared Sub Main() 
        Dim f1 As Func(Of Base, Derived) = AddressOf MyMethod
public class Base {}
public class Derived : Base {}

public class Program
{
    public static Derived MyMethod(Base b)
    {
        return b as Derived ?? new Derived();
    }

    static void Main() 
    {
        Func<Base, Derived> f1 = MyMethod;

الجزء الثاني من التعليمات البرمجية يُوضح أن المفوض يمكن تعيينه لمتغير من النوع Func<Base, Base> (Func(Of Base, Base) في Visual Basic) ، لأن نوع الإرجاع متباين تبايناً مشتركاً.

' Covariant return type.
Dim f2 As Func(Of Base, Base) = f1
Dim b2 As Base = f2(New Base())
// Covariant return type.
Func<Base, Base> f2 = f1;
Base b2 = f2(new Base());

الجزء الثالث من التعليمات البرمجية يوضح أن المفوض يمكن تعيينه إلى متغير من النوع Func<Derived, Derived> (Func(Of Derived, Derived) في Visual Basic) ، لأن نوع المعلمة متباين تبايناً عكسياً.

' Contravariant parameter type.
Dim f3 As Func(Of Derived, Derived) = f1
Dim d3 As Derived = f3(New Derived())
// Contravariant parameter type.
Func<Derived, Derived> f3 = f1;
Derived d3 = f3(new Derived());

الجزء الأخير من التعليمات البرمجية يوضح أن المفوض يمكن تعيينه إلى متغير من النوع Func<Derived, Base> (Func(Of Derived, Base) في Visual Basic) ، جامعاً بين تأثيرات نوع المعلمة المتباين تبايناً عكسياً و نوع الإرجاع المتباين تبايناً مشتركاً.

' Covariant return type and contravariant parameter type.
Dim f4 As Func(Of Derived, Base) = f1
Dim b4 As Base = f4(New Derived())
// Covariant return type and contravariant parameter type.
Func<Derived, Base> f4 = f1;
Base b4 = f4(new Derived());

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

في التعليمات البرمجية السابقة , توقيع الـ MyMethod يُطَابِق تماماً توقيع المفوض العام المُنشَئ : Func<Base, Derived> (Func(Of Base, Derived) في Visual Basic). المثال يوضح أن هذا المفوض يمكن تخزينه في متغيرات أو معلمات أسلوب لديها أنواع أكثر اشتقاقاً و أنواع إرجاع أقل اشتقاقاً , طالما أن كل أنواع المفوض يتم بناءها من نوع المفوض العام Func<T, TResult>.

هذا نقطة هامة. تأثيرات التباين المشترك و التباين العكسي في معلمات النوع للمفوضات العامة تشبه تأثيرات التباين المشترك و التباين العكسي في ربط المفوض العادي (راجع الفرق في المفوضون (C# و Visual Basic)). ومع ذلك ، التباين في ربط المفوض يعمل مع كافة أنواع المفوضات , و ليس فقط مع أنواع المفوضات العامة التي لها معلمات نوع متباينة. علاوة على ذلك ، التباين في ربط المفوض يُمَكِن ربط أسلوب بأي مفوض له أنواع معلمات أكثر تقيداً و أنواع إرجاع أقل تقيداً , بينما تعيين المفوضات العامة يعمل فقط إذا كان كلا نوعي المفوض مُنشَئة من نفس تعريف النوع العام.

المثال التالي يوضح التأثيرات المجتمعة للتباين في ربط المفوض و التباين في معلمات النوع العام. المثال يُعَرِف تسلسل هرمي للأنواع يتضمن ثلاثة أنواع , من الأقل اشتقاقاً ( Type1) إلى الأكثر اشتقاقاً (Type3). التباين في ربط المفوض العادي يُستَخدَم لربط أسلوب مع نوع معلمة Type1 و نوع إرجاع Type3 بمفوض عام مع نوع معلمة Type2 و نوع إرجاع Type2. المفوض العام الناتج يتم تعيينه عندها إلى متغير آخر نوع مفوضه العام لديه معلمة من النوع Type3 و نوع إرجاع Type1 , باستخدام التباين المشترك و التباين العكسي لمعلمات النوع العام. التعيين الثاني يتطلب كلا من نوع المتغير و نوع المفوض أن يكونا منشئان من نفس تعريف النوع العام ، في هذه الحالة، Func<T, TResult>.

Public Class Type1 
End Class
Public Class Type2
    Inherits Type1
End Class
Public Class Type3
    Inherits Type2
End Class

Public Class Program
    Public Shared Function MyMethod(ByVal t As Type1) As Type3
        Return If(TypeOf t Is Type3, t, New Type3())
    End Function

    Shared Sub Main() 
        Dim f1 As Func(Of Type2, Type2) = AddressOf MyMethod

        ' Covariant return type and contravariant parameter type.
        Dim f2 As Func(Of Type3, Type1) = f1
        Dim t1 As Type1 = f2(New Type3())
    End Sub
End Class
using System;

public class Type1 {}
public class Type2 : Type1 {}
public class Type3 : Type2 {}

public class Program
{
    public static Type3 MyMethod(Type1 t)
    {
        return t as Type3 ?? new Type3();
    }

    static void Main() 
    {
        Func<Type2, Type2> f1 = MyMethod;

        // Covariant return type and contravariant parameter type.
        Func<Type3, Type1> f2 = f1;
        Type1 t1 = f2(new Type3());
    }
}

العودة إلى الأعلى

تعريف واجهات و مفوضات عامة متباينة

بدءاً بـ .NET Framework 4 , Visual Basic و C# تحتوي على كلمات مفتاحية تُمَكِنُك من وضع علامة على المعلمات النوع العام للواجهات و المفوضات كمتباين تبايناً مشتركاً أو متباين تبايناً عكسياً.

ملاحظةملاحظة

بدءاً من .NET Framework الإصدار 2.0 , وقت تشغيل اللغة العامة يدعم التعليقات التوضيحية للتباين على معلمات النوع العام.ما قبل .NET Framework 4 ، الطريقة الوحيدة لتعريف فئة عامة لديها تلك التعليقات التوضيحية هي استخدام لغة Microsoft الوسيطة (MSIL) , إما عن طريق تحويل الفئة برمجياً بـ Ilasm.exe ( المجمع MSIL) أو بواسطة بعثها في تجميع حيوي.

معلمة النوع المتباينة تبايناً مشتركاً تم وضع علامة عليها بالكلمة المفتاحية out (الكلمة المفتاحية Out في Visual Basic، + لـمجمع MSIL). يمكنك استخدام معلمة نوع متباينة تبايناً مشتركاً كقيمة الإرجاع لأسلوب ينتمي إلى واجهة أو كنوع الإرجاع لمفوض. لا يمكنك استخدام معلمة نوع متباينة تبايناً مشتركاً كقيد نوع عام لأساليب الواجهة.

ملاحظةملاحظة

إذا كان أسلوب من واجهة لديه معلمة هي نوع مفوض عام ، معلمة النوع المتباينة تبايناً مشتركاً للواجهة يمكن استخدامها لتحديد معلمة نوع متباينة تبايناً عكسياً لنوع التفويض.

معلمة النوع المتباينة تبايناً عكسياً يتم وضع علامة عليها بالكلمة المفتاحية in (الكلمة المفتاحية In في Visual Basic، - لـمجمع MSIL). يمكنك استخدام معلمة نوع متباينة تبايناً عكسياً كنوع معلمة لأسلوب ينتمي إلى واجهة أو كنوع معلمة مفوض. يمكنك استخدام معلمة نوع متباينة تبايناً عكسياً كقيد نوع عام لأسلوب واجهة.

فقط أنواع الواجهة و أنواع المفوض يمكن أن يكون لها معلمات نوع متباينة. نوع الواجهة أو المفوض يمكن أن يكون لديه كلا معلمات النوع المتباينة تبايناً مشتركاً و المتباينة تبايناً عكسياً .

Visual Basic و C# لا تسمح لك بانتهاك قواعد استخدام معلمات النوع المتباينة تبايناً مشتركاً و المتباينة تبايناً عكسياً , أو إضافة تعليقات التباين المشترك و التباين العكسي التوضيحية إلى معلمات النوع لأنواع غير الواجهات و المفوضات. مجمع MSIL لا يقوم بتحققات مماثلة , و لكن TypeLoadException يتم طرحه إذا حاولت تحميل نوع ينتهك القواعد.

للحصول على معلومات و مثال تعليمات برمجية , راجع الالفرق في الواجهات العامة (C# و Visual Basic).

العودة إلى الأعلى

قائمة بأنواع الواجهات و المفوضات العامة المتباينة

في الـ .NET Framework 4 ، أنواع المفوض و الواجهة التالية لديها أنواع معلمات متباينة تبايناً مشتركاً و/أو متباينة تبايناً عكسياً.

Type

أنواع المعلمات المتباينة تبايناً مشتركاً.

أنواع المعلمات المتباينة تبايناً عكسياً.

Action<T> إلى Action<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16>

نعم

Comparison<T>

نعم

Converter<TInput, TOutput>

نعم

نعم

Func<TResult>

نعم

Func<T, TResult> إلى Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, TResult>

نعم

نعم

IComparable<T>

نعم

Predicate<T>

نعم

IComparer<T>

نعم

IEnumerable<T>

نعم

IEnumerator<T>

نعم

IEqualityComparer<T>

نعم

IGrouping<TKey, TElement>

نعم

IOrderedEnumerable<TElement>

نعم

IOrderedQueryable<T>

نعم

IQueryable<T>

نعم

العودة إلى الأعلى

راجع أيضًا:

المبادئ

الفرق في المفوضون (C# و Visual Basic)

موارد أخرى

التباين المشترك و Contravariance في (C#و Visual Basic)