[C#] لماذا لا يدعم التباين و contravariance نوع القيمة


Answers

ربما يكون من الأسهل فهم ما إذا كنت تفكر في التمثيل الأساسي (على الرغم من أن هذا هو بالفعل تفاصيل التنفيذ). هنا مجموعة من السلاسل:

IEnumerable<string> strings = new[] { "A", "B", "C" };

يمكنك التفكير في strings باعتبارها تحتوي على التمثيل التالي:

[0] : string reference -> "A"
[1] : string reference -> "B"
[2] : string reference -> "C"

وهي عبارة عن مجموعة من ثلاثة عناصر ، كل منها يشير إلى سلسلة. يمكنك إرسال هذا إلى مجموعة من الكائنات:

IEnumerable<object> objects = (IEnumerable<object>) strings;

في الأساس هو نفس التمثيل باستثناء الآن المراجع هي مراجع الكائن:

[0] : object reference -> "A"
[1] : object reference -> "B"
[2] : object reference -> "C"

التمثيل هو نفسه. يتم التعامل مع المراجع بشكل مختلف. لم يعد يمكنك الوصول إلى خاصية string.Length ولكن لا يزال يمكنك استدعاء object.GetHashCode() . قارن هذا بمجموعة من النقط:

IEnumerable<int> ints = new[] { 1, 2, 3 };
[0] : int = 1
[1] : int = 2
[2] : int = 3

لتحويل هذا إلى IEnumerable<object> يجب تحويل البيانات بواسطة boxing في ints:

[0] : object reference -> 1
[1] : object reference -> 2
[2] : object reference -> 3

يتطلب هذا التحويل أكثر من المدلى بها.

Question

IEnumerable<T> هو co-variant ولكنه لا يدعم نوع القيمة ، فقط نوع المرجع فقط. يتم تصنيف الشفرة البسيطة التالية بنجاح:

IEnumerable<string> strList = new List<string>();
IEnumerable<object> objList = strList;

ولكن سيؤدي التغيير من string إلى int إلى الحصول على خطأ برمجي:

IEnumerable<int> intList = new List<int>();
IEnumerable<object> objList = intList;

تم توضيح السبب في MSDN :

ينطبق التباين فقط على الأنواع المرجعية ؛ إذا قمت بتحديد نوع قيمة لمعلمة نوع المتغير ، فإن معلمة النوع هذه ثابتة عن النوع الذي تم إنشاؤه الناتج.

لقد بحثت ووجدت أن بعض الأسئلة ذكرت أن السبب هو الملاكمة بين نوع القيمة ونوع المرجع . لكنها لا تزال توضح رأيي لماذا السبب في الملاكمة؟

هل يمكن لشخص ما أن يقدم تفسيراً بسيطاً ومفصلاً لماذا لا يدعم التباين والمنافسة نوع القيمة وكيف يؤثر الملاكمة على ذلك؟




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

إذا قمت بإجبار أنواع القيم على التعامل معها على أنها أنواع مرجعية (أي ، قم بوضعها على سبيل المثال ، من خلال الإشارة إليها من خلال واجهة) ، يمكنك الحصول على تباين.

الطريقة الأسهل لمعرفة الفرق هي ببساطة النظر في Array : يتم وضع مصفوفة من أنواع القيم معًا في الذاكرة بشكل متقارب (مباشرة) ، حيث لا تحتوي صفيف أنواع المرجع إلا على مرجع (مؤشر) متزامن في الذاكرة ؛ يتم تخصيص الكائنات التي يتم توجيهها بشكل منفصل.

والقضية الأخرى (ذات الصلة) (*) هي أنه (تقريبًا) جميع أنواع المراجع لها نفس التمثيل لأغراض التباين ولا يحتاج الكثير من الكود إلى معرفة الفرق بين الأنواع ، لذلك يكون التباين المشترك والأوتوماتي ممكنًا (وبسهولة) تنفيذ - في كثير من الأحيان فقط عن طريق إغفال التحقق من نوع إضافي).

(*) يمكن أن ينظر إليه على أنه نفس المشكلة ...