التباين والمكافحة في لغات البرمجة
-
18-09-2019 - |
سؤال
هل يمكن لأي شخص أن يفسرني، مفهوم التباين والمكافحة في نظرية لغات البرمجة؟
المحلول
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.
لمزيد من الراحة الإضافية، إليك قائمة طلبات مرتبطة بكل مقالات إريك ليببيرت بشأن التباين:
- التباين والمكافحة في C #، الجزء الأول
- التباين والمكافحة في C #، الجزء الثاني: صفيف التباين
- التباين والمكافحة في C #، الجزء الثالث: طريقة تباين تحويل المجموعة
- التباين والمكافحة في C #، الجزء الرابع: تفاوت مندوب حقيقي
- التباين والمكافآت في C #، الجزء الخامس: وظائف النظام العالي تؤذي عقلي
- التباين والمكافحة في C #، الجزء السادس: التباين واجهة
- التباين والمكافحة في C # الجزء سبعة: لماذا نحتاج إلى بناء جملة على الإطلاق؟
- التباين والمكافحة في C #، الجزء الثامن: خيارات بناء الجملة
- التباين والمكافحة في C #، الجزء التاسع: كسر التغييرات
- التباين والمكافحة في C #، الجزء العاشر: التعامل مع الغموض
هناك تمييز يجري بين 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 # خطأ إذا حاولت القيام بشيء غير مدعوم.