سؤال

تم تنفيذ التباين العام في C # 4.0 بطريقة من الممكن كتابة ما يلي دون استثناء (وهو ما يحدث في C # 3.0):

 List<int> intList = new List<int>();
 List<object> objectList = intList; 

مثال غير وظيفي: انظر إجابة جون Skeet

لقد حضرت مؤتمرا مؤتمرا حيث أعطى Jon Skeet نظرة عامة ممتازة على التباين العام، لكنني لست متأكدا من أنني أحصل عليه تماما - أفهم أهمية in و out الكلمات الأساسية عندما يتعلق الأمر بالاهتمام والتباين المشترك، لكنني فضولي لما يحدث وراء الكواليس.

ماذا ترى CLR عند تنفيذ هذا الرمز؟ هل يحول ضمنيا List<int> ل List<object> أم أنها ببساطة ببناء ذلك يمكننا الآن تحويلها بين الأنواع المشتقة إلى أنواع الوالدين؟

من الاهتمام، لماذا لم يكن هذا عرضا في الإصدارات السابقة وما هي الفائدة الرئيسية - أي استخدام العالم الحقيقي؟

مزيد من المعلومات حول هذا بريد بالنسبة للتباين العام (ولكن السؤال قديم للغاية، تبحث عن معلومات حقيقية وحديثة)

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

المحلول

لا، مثالك لن يعمل لمدة ثلاثة أسباب:

  • الطبقات (مثل List<T>) ثابتة؛ فقط المندوبين والواجهات متغيرون
  • بالنسبة للتباين إلى العمل، يجب على الواجهة استخدام المعلمة النوع فقط في اتجاه واحد (في متناقض، خارج بالنسبة للأبغاء)
  • أنواع القيمة غير معتمدة كحجج من النوع للتباين - لذلك لا يوجد كونفرفس IEnumerable<int> ل IEnumerable<object> علي سبيل المثال

(فشل التعليمات البرمجية في الترجمة في كل من C # 3.0 و 4.0 - ليس هناك استثناء.)

إذا هذا سيكون الشغل:

IEnumerable<string> strings = new List<string>();
IEnumerable<object> objects = strings;

يستخدم CLR فقط المرجع، دون تغيير - يتم إنشاء أي كائنات جديدة. لذلك إذا اتصلت objects.GetType() كنت لا تزال تحصل على List<string>.

أعتقد أنه لم يتم إدخاله في وقت سابق لأن مصممي اللغة لا يزال يتعين عليهم العمل تفاصيل كيفية تعريضه - لقد كان في CLR منذ V2.

الفوائد هي نفسها أوقات أخرى حيث تريد أن تكون قادرا على استخدام نوع واحد كآخر. لاستخدام نفس المثال الذي استخدمته يوم السبت الماضي، إذا كان لديك شيء ينفذ IComparer<Shape> لمقارنة الأشكال حسب المنطقة، من الجنون أنه لا يمكنك استخدام ذلك لفرز List<Circle> - إذا كان بإمكانه مقارنة أي شكلين، فيمكنه بالتأكيد مقارنة أي دوائر. اعتبارا من C # 4، سيكون هناك تحويل متناقض من IComparer<Shape> ل IComparer<Circle> لذلك يمكنك الاتصال circles.Sort(areaComparer).

نصائح أخرى

بعض الأفكار الإضافية.

ماذا ترى CLR عند تنفيذ هذا الرمز

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

الآن، لا يزال سؤال معقول أن نسأل كيف يعمل التباين وراء الكواليس عندما يعمل. الجواب هو: السبب في تقييد هذا الحجج من النوع المرجعي الذي يحتوي على واجهة معلمات وأنواع المندوبين لا شيئ يحدث وراء الكواليس. عندما تقول

object x = "hello";

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

عندما تقول:

IEnumerator<string> e1 = whatever;
IEnumerator<object> e2 = e1;

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

IEnumerator<string> e1 = whatever;
IEnumerator<object> e2 = (IEnumerator<object>)(object)e1;

الآن يجب أن تولد CLR فحصا أن E1 ينفذ بالفعل هذه الواجهة، ويجب أن يكون هذا الشيك ذكيا حول التعرف على التباين.

لكن السبب في أننا نستطيع الابتعاد عن واجهات مختلفة يجري تحويلات غير متصلة لأن توافق الواجب العادي هو بهذه الطريقة. ماذا ستستخدم E2 ل؟

object z = e2.Current;

يؤدي ذلك بت إلى إشارة إلى سلسلة. لقد أنشأنا بالفعل أن تلك متوافقة مع كائن دون تغيير.

لماذا لم يكن هذا قدم سابقا؟ كان لدينا ميزات أخرى للقيام بميزانية محدودة.

ما هي الفائدة الرئيسية؟ أن التحويلات من سلسلة سلسلة إلى تسلسل الكائن "فقط العمل".

من الاهتمام، لماذا لم يكن هذا عرضا في الإصدارات السابقة

الإصدارات الأولى (1.x) من .NET لم يكن لديها جنراء على الإطلاق، لذلك كان التباين العام بعيدا.

تجدر الإشارة إلى أنه في جميع إصدارات .NET، هناك صفيف Covariance. لسوء الحظ، إنه التباين غير آمن:

Apple[] apples = new [] { apple1, apple2 };
Fruit[] fruit = apples;
fruit[1] = new Orange(); // Oh snap! Runtime exception! Can't store an orange in an array of apples!

التباين المشترك والاهتمام في C # 4 آمن، ويمنع هذه المشكلة.

ما هي الفائدة الرئيسية - أي استخدام العالم الحقيقي؟

عدة مرات في التعليمات البرمجية، أنت تتطلب API تتوقع نوعا مضاعفا من الأساس (على سبيل المثال IEnumerable<Base>) ولكن كل ما لديك هو نوع مضخم من المستمدة (على سبيل المثال IEnumerable<Derived>).

في C # 2 و C # 3، كنت بحاجة إلى تحويل يدويا إلى IEnumerable<Base>, ، على الرغم من أنه يجب أن "فقط العمل". التباين المشترك والاهتمام يجعلها "مجرد عمل".

PS تمتص تماما أن إجابة skeet تتناول كل نقاط حلدي. لعنة أنت، skeet! :-) يبدو أنه أجاب هذا قبل, ، على أية حال.

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