مشاركة عبر


إنشاء متغير واجهات عام (C# و Visual Basic)

إمكانية تعريف معلمات نوع عام في الواجهات كطردي أو contravariant. التباين يسمح لأساليب الواجهة أكثر اشتقاق أنواع الإرجاع من تعريفه بواسطة النوع العام معلمات. Contravariance يسمح لأساليب الواجهات باحتواء أنواع وسيطة أقل اشتقاقاً من المحددة من قبل المعلمات العامة. الواجهة العامة التي تقييد معلمة النوع العام الطردية أو الcontravariant تسمى متغير .

ملاحظة

4 .NET Framework يقدم الدعم الالفرق العديد من الواجهات العامة الموجودة. للحصول على قائمة الواجهات متغير في .NET Framework راجع الالفرق في الواجهات العامة (C# و Visual Basic).

للتصريح واجهات متغير عام

تقوم بتعريف متغير عام واجهات بإستخدام inو outالكلمات الأساسية لـ معلمة نوع عام.

هام

المعلمات ByRef في Visual Basic وrefوoutلا يمكن أن تكون المعلمات متغيرة في C# . لا تعتمد أنواع القيم أيضاً الالفرق.

إمكانية تعريف نوع معلمة عام طردي بإستخدام الكلمة أساسية out. النوع الطردي يجب أن يحقق الشروط التالية:

  • يستخدم النوع فقط كنوع إرجاع لأساليب الواجهة فقط النوع وهو غير مستخدم كنوع من وسيطات الأسلوب. يتم توضيح ذلك في المثال التالي، حيث النوع يتم التصريح بالنوع Rكطردي.

    Interface ICovariant(Of Out R)
        Function GetSomething() As R
        ' The following statement generates a compiler error.
        ' Sub SetSomething(ByVal sampleArg As R)
    End Interface
    
    interface ICovariant<out R>
    {
        R GetSomething();
        // The following statement generates a compiler error.
        // void SetSometing(R sampleArg);
    
    }
    

    وجود استثناء واحد هو إلى هذه قاعدة. إذا كان لديك مفوض عام contravariant كمعلمة أسلوب يمكنك إستخدام النوع معلمة نوع عام الخاصة بالمفوض. يتم توضيح هذا بواسطة الأداة نوع Rفي المثال التالي. للمزيد من المعلومات، راجع الفرق في المفوضون (C# و Visual Basic) واستخدام الفرق Func و إجراء التفويضات العام (C# و Visual Basic).

    Interface ICovariant(Of Out R)
        Sub DoSomething(ByVal callback As Action(Of R))
    End Interface
    
    interface ICovariant<out R>
    {
        void DoSomething(Action<R> callback);
    }
    
  • لا يتم إستخدام النوع بشكل قيد عام لأساليب الواجهة. يتم توضيح ذلك في التعليمات البرمجية التالية.

    Interface ICovariant(Of Out R)
        ' The following statement generates a compiler error
        ' because you can use only contravariant or invariant types
        ' in generic contstraints.
        ' Sub DoSomething(Of T As R)()
    End Interface
    
    interface ICovariant<out R>
    {
        // The following statement generates a compiler error
        // because you can use only contravariant or invariant types
        // in generic contstraints.
        // void DoSomething<T>() where T : R;
    }
    

يمكنك تعريف معلمة النوع العام كطردي بإستخدام الكلمة أساسية in. يستخدم النوع فقط كنوع إرجاع لأساليب الواجهة فقط النوع وهو غير مستخدم كنوع من وسيطات الأسلوب. يمكنك أن تستخدم نوع contravariant للقيود العامة. التعليمة البرمجية التالية توضح يبين كيفية التعريف واجهة contravariant قيد عام وإستخدامه على أحد الأساليب الخاصة به.

Interface IContravariant(Of In A)
    Sub SetSomething(ByVal sampleArg As A)
    Sub DoSomething(Of T As A)()
    ' The following statement generates a compiler error.
    ' Function GetSomething() As A
End Interface
interface IContravariant<in A>
{
    void SetSomething(A sampleArg);
    void DoSomething<T>() where T : A;
    // The following statement generates a compiler error.
    // A GetSomething();            
}

من الممكن أيضاً دعم contravariance وغير التباين المشترك في نفس واجهة ولكن لمعلمات نوع آخر كما هو موضح في المثال التالي من تعليمات برمجية.

Interface IVariant(Of Out R, In A)
    Function GetSomething() As R
    Sub SetSomething(ByVal sampleArg As A)
    Function GetSetSomething(ByVal sampleArg As A) As R
End Interface
interface IVariant<out R, in A>
{
    R GetSomething();
    void SetSomething(A sampleArg);
    R GetSetSometings(A sampleArg);
}

في Visual Basic يتعذر تعريف الأحداث في الواجهات متغير بدون تعيين نوع التفويض. أيضاً، لا يمكن أن تتداخل واجهة متغير فئات أو تتضمن التعدادات بنيات إلا أنه يمكن أن تتداخل الواجهات. يتم توضيح ذلك في التعليمات البرمجية التالية.

Interface ICovariant(Of Out R)
    ' The following statement generates a compiler error.
    ' Event SampleEvent()
    ' The following statement specifies the delegate type and 
    ' does not generate an error.
    Event AnotherEvent As EventHandler

    ' The following statements generate compiler errors,
    ' because a variant interface cannot have
    ' nested enums, classes, or structures.

    'Enum SampleEnum : test : End Enum
    'Class SampleClass : End Class
    'Structure SampleStructure : Dim value As Integer : End Structure

    ' Variant interfaces can have nested interfaces.
    Interface INested : End Interface
End Interface

تطبيق واجهات متغير عام

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

Interface ICovariant(Of Out R)
    Function GetSomething() As R
End Interface

Class SampleImplementation(Of R)
    Implements ICovariant(Of R)
    Public Function GetSomething() As R _
    Implements ICovariant(Of R).GetSomething
        ' Some code.
    End Function
End Class
interface ICovariant<out R>
{
    R GetSomething();
}
class SampleImplementation<R> : ICovariant<R>
{
    public R GetSomething()
    {
        // Some code.
        return default(R);
    }
}

تعتبر الفئات التي تقوم بتنفيذ واجهات متغير ثابتة. على سبيل المثال، اطلع على التعليمات البرمجية التالية:

' The interface is covariant.
Dim ibutton As ICovariant(Of Button) =
    New SampleImplementation(Of Button)
Dim iobj As ICovariant(Of Object) = ibutton

' The class is invariant.
Dim button As SampleImplementation(Of Button) =
    New SampleImplementation(Of Button)
' The following statement generates a compiler error
' because classes are invariant.
' Dim obj As SampleImplementation(Of Object) = button
// The interface is covariant.
ICovariant<Button> ibutton = new SampleImplementation<Button>();
ICovariant<Object> iobj = ibutton;

// The class is invariant.
SampleImplementation<Button> button = new SampleImplementation<Button>();
// The following statement generates a compiler error
// because classes are invariant.
// SampleImplementation<Object> obj = button;

توسيع متغيريات واجهات عامة

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

Interface ICovariant(Of Out T)
End Interface

Interface IInvariant(Of T)
    Inherits ICovariant(Of T)
End Interface

Interface IExtCovariant(Of Out T)
    Inherits ICovariant(Of T)
End Interface
interface ICovariant<out T> { }
interface IInvariant<T> : ICovariant<T> { }
interface IExtCovariant<out T> : ICovariant<T> { }

في IInvariant<T>( Invariant(Of T)في Visual Basic) واجهة نوع معلمة عام Tهو ثابتة ، بينما في IExtCovariant<out T>( IExtCovariant (Of Out T)في Visual Basic) نوع معلمة طردي, على الرغم من أن كلاهما توسيع واجهات نفس الواجهة. يتم تطبيق القاعدة نفس إلى معلمة نوع عام contravariant .

يمكنك إنشاء واجهة التي تمد كلا الواجهات حيث معلمة النوع العام Tتكون طردية و الواجهة تكون contravariant إذا كان توسيع نوع معلمة عام Tinvariant. يتم توضيح هذا الإستخدام في المثال التالي.

Interface ICovariant(Of Out T)
End Interface

Interface IContravariant(Of In T)
End Interface

Interface IInvariant(Of T)
    Inherits ICovariant(Of T), IContravariant(Of T)
End Interface
interface ICovariant<out T> { }
interface IContravariant<in T> { }
interface IInvariant<T> : ICovariant<T>, IContravariant<T> { }

ومع ذلك، إذا كانت معلمة النوع العام Tيتم تعريف طردي في واجهة واحدة ، و يتعذر تعريف أنه contravariant في الواجهةالممتدة أو على العكس. يتم توضيح هذا الإستخدام في المثال التالي.

Interface ICovariant(Of Out T)
End Interface

' The following statements generate a compiler error.
' Interface ICoContraVariant(Of In T)
'     Inherits ICovariant(Of T)
' End Interface
interface ICovariant<out T> { }
// The following statement generates a compiler error.
// interface ICoContraVariant<in T> : ICovariant<T> { }

تجنب الغموض

عند تطبيق واجهات متغير عام الالفرق أحياناً يمكن أن يؤدي إلى التباس. يجب تجنب هذا.

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

ملاحظة

مع Option Strict Off، ينشئ Visual Basic تحذير المحول برمجي عند وجود تطبيق واجهة يدعو للالتباس. بإستخدام Option Strict Onينشئ Visual Basic خطأ المحول البرمجي.

' Simple class hierarchy.
Class Animal
End Class

Class Cat
    Inherits Animal
End Class

Class Dog
    Inherits Animal
End Class

' This class introduces ambiguity
' because IEnumerable(Of Out T) is covariant.
Class Pets
    Implements IEnumerable(Of Cat), IEnumerable(Of Dog)

    Public Function GetEnumerator() As IEnumerator(Of Cat) _
        Implements IEnumerable(Of Cat).GetEnumerator
        Console.WriteLine("Cat")
        ' Some code.
    End Function

    Public Function GetEnumerator1() As IEnumerator(Of Dog) _
        Implements IEnumerable(Of Dog).GetEnumerator
        Console.WriteLine("Dog")
        ' Some code.
    End Function

    Public Function GetEnumerator2() As IEnumerator _
        Implements IEnumerable.GetEnumerator
        ' Some code.
    End Function
End Class

Sub Main()
    Dim pets As IEnumerable(Of Animal) = New Pets()
    pets.GetEnumerator()
End Sub
// Simple class hierarchy.
class Animal { }
class Cat : Animal { }
class Dog : Animal { }

// This class introduces ambiguity
// because IEnumerable<out T> is covariant.
class Pets : IEnumerable<Cat>, IEnumerable<Dog>
{
    IEnumerator<Cat> IEnumerable<Cat>.GetEnumerator()
    {
        Console.WriteLine("Cat");
        // Some code.
        return null;
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        // Some code.
        return null;
    }

    IEnumerator<Dog> IEnumerable<Dog>.GetEnumerator()
    {
        Console.WriteLine("Dog");
        // Some code.
        return null;
    }
}
class Program
{
    public static void Test()
    {
        IEnumerable<Animal> pets = new Pets();
        pets.GetEnumerator();
    }
}

في هذا المثال، لا يتم تحديد كيفية اختيار الأسلوب pets.GetEnumerator بين Catو Dog. وقد يتسبب هذا في حدوث مشاكل في تعليمات برمجية.

راجع أيضًا:

المرجع

استخدام الفرق Func و إجراء التفويضات العام (C# و Visual Basic)

المبادئ

الالفرق في الواجهات العامة (C# و Visual Basic)