سؤال

هل يمكن لأي شخص أن يفسرني، مفهوم التباين والمكافحة في نظرية لغات البرمجة؟

هل كانت مفيدة؟

المحلول

covariance. هو بسيط جدا وأفضل فكر من منظور بعض فئة المجموعة List. وبعد نحن نقدر parameterize. ال List فئة مع بعض المعلمات النوع T. وبعد وهذا هو، قائمتنا تحتوي على عناصر من النوع T لبعض T. وبعد قائمة سيكون covariant إذا

S هو نوع فرعي من قائمة TFF [S] هو نوع فرعي من القائمة [T

(حيث أستخدم التعريف الرياضي IFF. يعني إذا وفقط إذا.)

هذا هو List[Apple] هو List[Fruit]. وبعد إذا كان هناك بعض الروتين الذي يقبل List[Fruit] كمعلمة، ولدي List[Apple], ، ثم يمكنني تمرير هذا بمعلمة صالحة.

def something(l: List[Fruit]) {
    l.add(new Pear())
}

إذا فئة مجموعتنا List غير قابل للتغيير، ثم لا معنى للتبلور لأننا قد نفترض أن روتيننا يمكن أن يضيف بعض الفاكهة الأخرى (التي لم تكن تفاحة) على النحو الوارد أعلاه. وبالتالي يجب أن نحب فقط ثابت مجموعات جمع أن تكون covariant!

نصائح أخرى

فيما يلي مقالاتي حول كيفية إضافة ميزات التباين الجديدة إلى C # 4.0. ابدأ من القاع.

http://blogs.msdn.com/ericlippert/archive/tags/covariance+and+contravariace/default.aspx.

هناك تمييز يجري بين covariance. و متناقض.
تقريبا تقريبا، عملية Covarian إذا حافظت على طلب الأنواع، ومكافحة إذا كان ذلك انعكاس هذا الطلب.

يهدف الطلب نفسه إلى تمثيل المزيد من أنواع عامة أكبر من أنواع أكثر تحديدا.
إليك مثال واحد على الموقف الذي يدعم فيه C # التباين. أولا، هذه مجموعة من الكائنات:

object[] objects=new object[3];
objects[0]=new object();
objects[1]="Just a string";
objects[2]=10;

بالطبع، من الممكن إدراج قيم مختلفة في صفيف لأنه في النهاية يستمدون جميعا من System.Object في .NET Framework. بعبارات أخرى، System.Object هو عام جدا أو كبير اكتب. الآن ها هي بقعة حيث يتم دعم التبلور:
تعيين قيمة من نوع أصغر إلى متغير من نوع أكبر

string[] strings=new string[] { "one", "two", "three" };
objects=strings;

الكائنات المتغيرة، والتي من النوع object[], ، يمكن تخزين قيمة في الواقع من النوع string[].

فكر في الأمر - إلى حد ما، فهذا ما تتوقعه، ولكن بعد ذلك مرة أخرى ليس كذلك. بعد كل شيء، في حين string مستمد من object, string[] لا يشتق من object[]. وبعد إن الدعم اللغوي للأبغاء في هذا المثال يجعل المهمة ممكنة على أي حال، وهو شيء ستجده في كثير من الحالات. فرق هي ميزة تجعل اللغة تعمل بشكل أكثر بساطة.

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

// Runtime exception here - the array is still of type string[],
// ints can't be inserted
objects[2]=10;

// Compiler error here - covariance support in this scenario only
// covers reference types, and int is a value type
int[] ints=new int[] { 1, 2, 3 };
objects=ints;

مثال لأعمال متناقض أكثر تعقيدا بعض الشيء. تخيل هاتين الطبقتين:

public partial class Person: IPerson {
    public Person() {
    }
}

public partial class Woman: Person {
    public Woman() {
    }
}

Woman مشتق من Person, ، بوضوح. الآن فكر في أن لديك هذين الوظيفتين:

static void WorkWithPerson(Person person) {
}

static void WorkWithWoman(Woman woman) {
}

واحدة من الوظائف تفعل شيئا (لا يهم ماذا) مع Woman, والآخر هو أكثر عمومية ويمكن أن تعمل مع أي نوع مشتق من Person. وبعد على ال Woman جانب من الأشياء، لديك الآن أيضا هذه:

delegate void AcceptWomanDelegate(Woman person);

static void DoWork(Woman woman, AcceptWomanDelegate acceptWoman) {
    acceptWoman(woman);
}

DoWork هي وظيفة يمكن أن تأخذ Woman وإشارة إلى وظيفة تأخذ أيضا Woman, ، ثم يمر مثيل Woman للمندوب. النظر في تعدد الأشكال من العناصر التي لديك هنا. Person يكون أكبر من Woman, ، و WorkWithPerson يكون أكبر من WorkWithWoman. WorkWithPerson ويعتبر أيضا أكبر من AcceptWomanDelegate لغرض التباين.

أخيرا، لديك هذه الخطوط الثلاثة من التعليمات البرمجية:

Woman woman=new Woman();
DoWork(woman, WorkWithWoman);
DoWork(woman, WorkWithPerson);

أ Woman يتم إنشاء مثيل. ثم يسمى dowork، تمر في Woman مثيل وكذلك مرجع إلى WorkWithWoman طريقة. هذا الأخير متوافق مع نوع المفوض AcceptWomanDelegate - معلمة واحدة من النوع Woman, ، لا نوع العودة. السطر الثالث غريب بعض الشيء، رغم ذلك. طريقة WorkWithPerson يأخذ Person كما المعلمة، وليس Woman, ، كما هو مطلوب من قبل AcceptWomanDelegate. وبعد مع ذلك، WorkWithPerson متوافق مع نوع المفوض. متناقض يجعل من الممكن، لذلك في حالة المندوبين نوع أكبر WorkWithPerson يمكن تخزينها في متغير من النوع الأصغر AcceptWomanDelegate. وبعد مرة واحدة أكثر شيء بديهية: إذا WorkWithPerson يمكن أن تعمل مع أي Person, ، تمر Woman لا يمكن أن يكون خطأ, ، الصحيح؟

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

List<object> objectList=new List<object>();
List<string> stringList=new List<string>();
objectList=stringList;

إذا جربت ذلك، فستجد أن هذا ليس سيناريو مدعوم في C #. في C # الإصدار 4.0 بالإضافة إلى .NET Framework 4.0، تم تنظيف دعم التباين في الأجداد، ومن الممكن الآن استخدام الكلمات الرئيسية الجديدة في و خارج مع المعلمات نوع عام. يمكنهم تحديد وتقييد اتجاه تدفق البيانات لمعلمة نوع معين، مما يتيح التباين للعمل. ولكن في حالة List<T>, ، بيانات النوع T يتدفق في كلا الاتجاهين - هناك طرق على النوع List<T> هذا العودة T القيم وغيرها من تلقي هذه القيم.

نقطة هذه القيود الاتجاهية هي للسماح بالتباين حيث من المنطقي, ، لكن من أجل منع المشاكل مثل خطأ وقت التشغيل المذكور في واحدة من أمثلة الصفيف السابقة. عندما يتم تزيين معلمات الكتابة بشكل صحيح مع في أو خارج, ، يمكن للمترجم التحقق، والسماح أو عدم السماح، والتباين منه في وقت الترجمة. وبعد لقد ذهبت Microsoft إلى جهد إضافة هذه الكلمات الرئيسية إلى العديد من الواجهات القياسية في .NET Framework، مثل IEnumerable<T>:

public interface IEnumerable<out T>: IEnumerable {
    // ...
}

لهذه الواجهة، تدفق البيانات من النوع T الكائنات واضحة: لا يمكن استرجاعهم فقط من الأساليب المدعومة من هذه الواجهة، وليس مرت فيها. وبعد نتيجة لذلك، من الممكن بناء مثال مشابه ل List<T> المحاولة الموصوفة سابقا، ولكن باستخدام IEnumerable<T> :

IEnumerable<object> objectSequence=new List<object>();
IEnumerable<string> stringSequence=new List<string>();
objectSequence=stringSequence;

هذا الرمز مقبول للمترجم C # منذ الإصدار 4.0 لأن IEnumerable<T> هو covarian بسبب خارج محدد في المعلمة النوع T.

عند العمل مع أنواع عامة، من المهم أن تكون على دراية بالتباين والطريقة التي يقوم بها المحول البرمجي بتطبيق أنواع مختلفة من الخداع من أجل جعل التعليمات البرمجية الخاصة بك بالطريقة التي تتوقعها.

هناك المزيد لمعرفة التباين مما هو مغطى في هذا الفصل، ولكن هذا يجب أن يكفي لجعل كل رمز مزيد من التعليمات البرمجية.

المرجع:

يحتوي بارت دي سبليت على إدخال مدونة رائعة حول التباين والمكافحة هنا.

يتيح كل من C # و CLR عن التباين والتعاوت من أنواع المراجع عند ربط طريقة للمندوب. تعني التباين أن الطريقة يمكن أن تعيد نوعا مشتقا من نوع عودة المندوب. تعني كونترا التباين أن الطريقة يمكن أن تتخذ معلمة هي قاعدة من نوع المعلمة المفوض. على سبيل المثال، نظرا للمندوب المحدد مثل هذا:

مندوب كائن MyCallback (FileStream S)؛

من الممكن إنشاء مثيل من نوع المندوب الذي يرتبط به طريقة النماذج الأولية

مثله:

سلسلة somemethod (stream s)؛

هنا، نوع عودة Somemethod (String) هو نوع مشتق من نوع عودة المندوب (كائن)؛ يسمح هذا التباين. نوع المعلمة Somemethod هو نوع هو فئة أساسية من نوع المعلمة المفوض (FileStream)؛ يسمح هذا التباين في كونترا.

لاحظ أن التباين والتباين المتنافسين مدعومون فقط لأنواع المرجعية، وليس لأنواع القيمة أو باطل. لذلك، على سبيل المثال، لا يمكنني ربط الطريقة التالية بفوضى MyCallback:

Int32 Someothermetmethod (Stream S)؛

على الرغم من أن نوع إرجاع Someothermethethod (Int32) مشتق من نوع عودة MyCallback (كائن)، إلا أن هذا النموذج من Covariance غير مسموح به لأن Int32 هو نوع القيمة.

من الواضح أن السبب وراء استخدام أنواع القيمة وبطاطا لا يمكن استخدامها في التباين والتخلي عن كونترا لأن هيكل الذاكرة لهذه الأشياء تختلف، في حين أن هيكل الذاكرة لأنواع المرجعية هي دائما مؤشر. لحسن الحظ، سيقوم برنامج التحويل البرمجي C # خطأ إذا حاولت القيام بشيء غير مدعوم.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top