سؤال

إذا كان لدي هذين الفئتين:

class A {}
class B : A {}

وأقوم بإنشاء قائمة <A> ولكني أريد إضافة قائمة <B> إليها عن طريق استدعاء List<A>.AddRange(List<B>) لكن المترجم يرفض:

Argument '1': cannot convert from 'System.Collections.Generic.List<A>'
to 'System.Collections.Generic.IEnumerable<B>

وهو ما أفهمه تمامًا نظرًا لأن IEnumerable<B> لا يرث من IEnumerable<A>، فإن نوعه العام له الميراث.

الحل الخاص بي هو التعداد من خلال القائمة<B> وإضافة العناصر بشكل فردي لأن القائمة<A>.Add(A item) ستعمل مع العناصر B:

foreach(B item in listOfBItems)
{
    listOfAItems.Add(item);
}

ومع ذلك، هذا غير معبر إلى حد ما لأن ما أريده هو AddRange فقط.

يمكن أن أستخدم

List<B>.ConvertAll<A>(delegate(B item) {return (A)item;});

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

سؤال:إذا كنت سأكتب مجموعتي التي تشبه القائمة، ما هي الطريقة التي سأضيفها إليها والتي ستسمح لي بنسخ مجموعة من B إلى مجموعة من A كخط واحد يشبه List<A>.AddRange(List<B >) و الاحتفاظ بأقصى قدر من السلامة من النوع.(وأعني بالحد الأقصى أن الوسيطة عبارة عن فحص للتجميع والنوع.)

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

المحلول

في الواقع، الأنواع العامة ليست متنوعة في الوقت الحالي.في C# 4.0، IEnumerable<B> سيكون قابلاً للتحويل إلى IEnumerable<A> إذا كان B قابلاً للتحويل إلى A عبر تحويل مرجعي.للحصول على بعض التفاصيل حول تصميم هذه الميزة، راجع:

http://blogs.msdn.com/ericlippert/archive/tags/Covariance+and+Contravariance/default.aspx

نصائح أخرى

هذا لا يعمل لسوء الحظ لأن الأدوية العامة في .net لا تدعم (حتى الآن) التباين المشترك.

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

إذا قمت بتنفيذ فئة القائمة الخاصة بك، فيمكنك إضافة التباين باستخدام معلمة عامة إضافية:

class MyList<T> {
    void AddRange<U>(IEnumerable<U> items) where U: T {
        foreach (U item in items) {
            Add(item);
        }
    }
}

ألا يمكنك أن تفعل فقط:

listOfAItems.AddRange(listOfBItems.Cast<A>());

لقد تمكنت من تحقيق ذلك باستخدام LINQ ...

listOfAItems.AddRange(listOfBItems.Cast<A>());

في حال وجدت نفسك في موقف لا تتنوع فيه الأنواع العامة، فإن طريقة الامتداد التالية يمكن أن تجعل حياتك أسهل:

public static void AddRange<TList,TOther>(this List<TList> list, IEnumerable<TOther> collection) where TOther: TList {
    foreach(TOther e in collection) {
        list.Add(e);
    }
}

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

List<Animal> animals;
List<Dog> dogs;
animals.AddRange(dogs);

الشيء الوحيد الذي يمكنني التوصل إليه هو هذا

public class MyList<T> : List<T>
{
    public void AddRange<Tother>(IEnumerable<Tother> col)
        where Tother: T
    {    
        foreach (Tother item in col)
        {
            this.Add(item);
        }
    }
}

الاتصال به يعني القيام بـ MyList<A>.AddRange<B>(MyList<B>).يفشل هذا إذا كانت الوسيطة غير قابلة للتعداد أو إذا لم ينجح وراثة النوع بحيث يلبي الحد الأقصى لمتطلبات أمان النوع الخاصة بسؤالي.

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