في C#، لماذا لا يمكن تخزين كائن List<string> في متغير List<object>

StackOverflow https://stackoverflow.com/questions/6557

سؤال

يبدو أن كائن القائمة لا يمكن تخزينه في متغير قائمة في لغة C#، ولا يمكن حتى أن يتم صياغته بهذه الطريقة بشكل صريح.

List<string> sl = new List<string>();
List<object> ol;
ol = sl;

ينتج عنه لا يمكن تحويل النوع ضمنيًا System.Collections.Generic.List<string> ل System.Collections.Generic.List<object>

وثم...

List<string> sl = new List<string>();
List<object> ol;
ol = (List<object>)sl;

النتائج لا يمكن تحويل النوع System.Collections.Generic.List<string> ل System.Collections.Generic.List<object>

بالطبع، يمكنك القيام بذلك عن طريق سحب كل شيء من قائمة السلاسل وإعادته مرة أخرى، واحدًا تلو الآخر، لكنه حل معقد إلى حد ما.

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

المحلول

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

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

List<object> ol = new List<object>();
List<string> sl;
sl = (List<string>)ol;

لم أستخدم C# منذ فترة، لذلك لا أعرف ما إذا كان هذا قانونيًا، ولكن هذا النوع من الممثلين مفيد بالفعل (من المحتمل).في هذه الحالة، أنت تنتقل من فئة (كائن) أكثر عمومية إلى فئة (سلسلة) أكثر تحديدًا تمتد من الفئة العامة.بهذه الطريقة، إذا أضفت إلى قائمة السلاسل، فإنك لا تنتهك قائمة الكائنات.

هل يعرف أي شخص أو يمكنه اختبار ما إذا كان هذا التمثيل قانونيًا في لغة C#؟

نصائح أخرى

إذا كنت تستخدم .NET 3.5، فقم بإلقاء نظرة على طريقة Enumerable.Cast.إنها طريقة تمديد لذا يمكنك الاتصال بها مباشرة في القائمة.

List<string> sl = new List<string>();
IEnumerable<object> ol;
ol = sl.Cast<object>();

إنه ليس بالضبط ما طلبته ولكن يجب أن يقوم بالخدعة.

يحرر:كما لاحظ Zooba، يمكنك بعد ذلك الاتصال بـ ol.ToList() للحصول على قائمة

لا يمكنك التحويل بين الأنواع العامة بمعلمات نوع مختلفة.لا تشكل الأنواع العامة المتخصصة جزءًا من نفس شجرة الوراثة، وبالتالي فهي أنواع غير مرتبطة.

للقيام بذلك قبل NET 3.5:

List<string> sl = new List<string>();
// Add strings to sl

List<object> ol = new List<object>();

foreach(string s in sl)
{
    ol.Add((object)s);  // The cast is performed implicitly even if omitted
}

باستخدام لينك:

var sl = new List<string>();
// Add strings to sl

var ol = new List<object>(sl.Cast<object>());

// OR
var ol = sl.Cast<object>().ToList();

// OR (note that the cast to object here is required)
var ol = sl.Select(s => (object)s).ToList();

والسبب هو أن فئة عامة مثل List<> يتم التعامل معها، في معظم الأغراض، خارجيًا كفئة عادية.على سبيل المثالعندما تقول List<string>() يقول المترجم ListString() (الذي يحتوي على سلاسل).[الشعب الفني:هذه نسخة واضحة للغاية من اللغة الإنجليزية لما يحدث]

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

لهذا السبب توجد طرق تمديد لـ IEnumerable مثل Convert() التي تتيح لك توفير التحويل بسهولة للعناصر المخزنة داخل المجموعة، والتي يمكن أن تكون بسيطة مثل النقل من عنصر إلى آخر.

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

قد ترغب في تجربة كود ff بدلاً من ذلك:

List<string> sl = new List<string>();
//populate sl
List<object> ol = new List<object>(sl);

أو:

List<object> ol = new List<object>();
ol.AddRange(sl);

سيقوم ol (نظريًا) بنسخ جميع محتويات sl دون مشاكل.

نعم، يمكنك ذلك، من .NET 3.5:

List<string> sl = new List<string>();
List<object> ol = sl.Cast<object>().ToList();

مايك - أعتقد أن التناقض غير مسموح به في لغة C# أيضًا

يرى تباين معلمة النوع العام في CLR لمزيد من المعلومات.

أعتقد أن هذا (التناقض) سيتم دعمه بالفعل في الإصدار C# 4.0.http://blogs.msdn.com/charlie/archive/2008/10/27/linq-farm-covariance-and-contravariance-in-visual-studio-2010.aspx

هذا في الواقع حتى لا تحاول وضع أي "كائن" غريب في متغير القائمة "ol" الخاص بك (كما List<object> يبدو أنه يسمح) - لأن الكود الخاص بك سوف يتعطل بعد ذلك (لأن القائمة موجودة بالفعل List<string> وسوف يقبل فقط كائنات نوع السلسلة).لهذا السبب لا يمكنك تحويل المتغير الخاص بك إلى مواصفات أكثر عمومية.

في Java، يكون الأمر على العكس من ذلك، ليس لديك أدوية عامة، وبدلاً من ذلك كل شيء عبارة عن قائمة كائنات في وقت التشغيل، ويمكنك حقًا حشو أي كائن غريب في قائمتك التي يُفترض أنها مكتوبة بدقة.ابحث عن "Reified Generics" للاطلاع على مناقشة أوسع لمشكلة Java...

مثل هذا التباين في الأدوية العامة غير مدعوم، ولكن يمكنك فعل ذلك باستخدام المصفوفات:

object[] a = new string[] {"spam", "eggs"};

يقوم C# بإجراء فحوصات وقت التشغيل لمنعك من وضع ملف int داخل a.

إليك حل آخر لما قبل .NET 3.5 لأي قائمة يمكن عرض محتوياتها ضمنيًا.

public IList<B> ConvertIList<D, B>(IList<D> list) where D : B
{
    List<B> newList = new List<B>();

    foreach (D item in list)
    {
        newList.Add(item);
    }

    return newList;
}

(استنادًا إلى مثال زوبا)

انا املك:

private List<Leerling> Leerlingen = new List<Leerling>();

وكنت سأملأها بالبيانات التي تم جمعها في List<object>ما نجح أخيرًا بالنسبة لي هو هذا:

Leerlingen = (List<Leerling>)_DeserialiseerLeerlingen._TeSerialiserenObjecten.Cast<Leerling>();

.Cast إلى النوع الذي تريد الحصول عليه IEnumerable من هذا النوع، ثم قم بطباعة IEnemuerable إلى List<> انت تريد.

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

IEnumerable<object> ob;
List<string> st = new List<string>();
ob = st.Cast<object>();

والثاني هو تجنب نوع الكائن IEnumerable، فقط قم بإرسال السلسلة إلى نوع الكائن ثم استخدم الدالة "toList()" في نفس الجملة:

List<string> st = new List<string>();
List<object> ob = st.Cast<object>().ToList();

أنا أحب الطريقة الثانية أكثر.آمل أن يساعد هذا.

List<string> sl = new List<string>();
List<object> ol;
ol = new List<object>(sl);
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top