C # لا يمكن تحويل من IEnumerable <قاعدة> إلى IEnumerable <مشتقة>

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

سؤال

وأنا في الآونة الأخيرة واجهت مشاكل عند محاولة AddRange (IEnumerable) إلى قائمة. الأرجح مشكلة الكلاسيكية، ولكن أنا لا حقا الحصول عليها حتى الآن.

وأنا أفهم أن أساليب تتوقع المعلمة قائمة ليست راضية مع قائمة، لأنها قد محاولة إضافة قاعدة إلى قائمة، وهو أمر مستحيل بالطبع.

ولكن لو كنت الحصول على هذا صحيح، منذ IEnumerables أنفسهم لا يمكن تغييرها، فإنه ينبغي أن تعمل في هذه القضية.

والرمز فكرت يشبه هذا:

class Foo
{
}

class Bar : Foo
{
}

class FooCol
{
    private List<Foo> m_Foos = new List<Foo> ();

    public void AddRange1(IEnumerable<Foo> foos)
    {
        m_Foos.AddRange (foos); // does work
    }

    public void AddRange2<T>(IEnumerable<T> foos) where T : Foo
    {
        m_Foos.AddRange (foos); // does not work
    }
}

class Program
{
    static void Main(string[] args)
    {
        FooCol fooCol = new FooCol ();

        List<Foo> foos = new List<Foo> ();
        List<Bar> bars = new List<Bar> ();

        fooCol.AddRange1 (foos); // does work
        fooCol.AddRange1 (bars); // does not work

        fooCol.AddRange2 (foos); // does work
        fooCol.AddRange2 (bars); // does work
    }
}

وحاولت تمرير إشارة إلى مترجم في طريقة AddRange2، ولكن هذا فقط انتقلت إلى مشكلة حولها.

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

وIIRC، ودعم هذا النوع من العمليات تم إضافتها إلى جافا 1.5، لذلك ربما ستضاف إلى C # في مرحلة ما في المستقبل أيضا ...؟

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

المحلول

وهذا هو التغاير، وسوف تكون ثابتة في C # 4.0 / 4.0. NET. أما الآن، فإن خيار عام هو أفضل إجابة (لIEnumerable<T> - <وأ href = "http://marcgravell.blogspot.com/2009/02/what-c-40-covariance-doesn-do.html" يختلط = " noreferrer "> لا IList<T> الخ ).

ولكن في طريقة عامة، عليك أن تفكر من حيث T. يمكنك أيضا استخدام Cast<T> أو OfType<T> مع LINQ لتحقيق شيئا من هذا القبيل.

نصائح أخرى

في C # 3.0 يمكنك استخدام "الزهر" طريقة التمديد. إذا قمت باستيراد System.Linq ومن ثم استخدام هذا الرمز:

public void AddRange2<T>(IEnumerable<T> foos) where T : Foo
{
    m_Foos.AddRange (foos.Cast<Foo>());
}

وبعد ذلك يجب أن تعمل من أجلك.

وهناك مشكلة مع طريقة التمديد:

public static IEnumerable<TBase> ToBaseEnumerable<TBase, TDerived>( this IEnumerable<TDerived> items ) where TDerived : TBase {
    foreach( var item in items ) {
        yield return item;
    }
}
...
IEnumerable<Employee> employees = GetEmployees(); //Emplyoee derives from Person
DoSomethingWithPersons( employees.ToBaseEnumerable<Person, Employee>() );

ولكن "<شخص، الموظف>" هو القليل محرجا بعض الشيء: /

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

public static class UpTo<T>
{
    public static IEnumerable<T> From<F>(IEnumerable<F> source) where F:T
    {
        // this cast is guaranteed to work
        return source.Select(f => (T) f);
    }
}

والاستعمال:

والثدييات IEnumerable = تصل <الثدييات> .من (kennel.Dogs)

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