هل من الخطأ إلقاء تعداد لفئة الطفل على تعداد فئة الوالدين؟

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

  •  04-07-2019
  •  | 
  •  

سؤال

لدي خطأ في بنيتي يقول:

خطأ 12 لا يمكن تحويل نوع "system.collections.generic.ienumerator <ScEclass> إلى" System.Collections.generic.ienumerator <ParentClass> ". يوجد تحويل صريح (هل تفتقد ممثلين؟)

هل من الخطأ ببساطة إبعاده؟

هذا هو الكود الخاص بي:

public Dictionary<Int32, BaseClass> Map { get; private set; }

public IEnumerator<BaseClass> GetEnumerator()
        {
            return this.Map.Values.GetEnumerator();
        }

public IEnumerator<IParentClass> IEnumerable<IParentClass>.GetEnumerator()
        {
            return this.GetEnumerator(); // ERROR!
        }

سؤالي هو ، هل يمكنني تغيير هذا السطر فقط:

return this.GetEnumerator();

إلى:

return (IEnumerator<IParentClass>)this.GetEnumerator();

(بدون أي آثار جانبية سيئة)؟

إجابة مقبولة:
لقد قمت بتغيير الوظيفة إلى ما يلي (بعد قراءة منشور Jon Skeet):

IEnumerator<IParentClass> IEnumerable<IParentClass>.GetEnumerator()
        {
            return this.Map.Values.Cast<IParentClass>().GetEnumerator();
        }
هل كانت مفيدة؟

المحلول

لا ، لا يمكنك ذلك ، لأن الأدوية غير متغيرة في C# في الوقت الحالي. .NET نفسه لديه بعض الدعم (للمندوبين والواجهات) ولكنه لم يستخدم حقًا بعد.

إذا كنت تعود IEnumerable<BaseClass> بدلاً من IEnumerator<BaseClass> (وافتراض .NET 3.5) يمكنك استخدامه Enumerable.Cast - ولكن ستحتاج حاليًا إلى كتابة طريقة التمديد الخاصة بك ، على سبيل المثال

public static IEnumerator<TParent> Upcast<TParent, TChild>
    (this IEnumerator<TChild> source)
    where TChild : TParent
{
    while (source.MoveNext())
    {
        yield return source.Current;
    }
}

بدلاً من ذلك في حالتك ، يمكنك استخدام CAST في وقت سابق:

return this.Map.Values.Cast<BaseClass>().GetEnumerator();

نصائح أخرى

لا ، لا يمكنك ، على الأقل في C# 3.0 وتحت تباين الواجهة غير مدعوم. انظر سلسلة Eric Lippert الممتازة على هذا ، وعلى وجه التحديد هذا.

لا ، إنها ليست آمنة ، انظر أدناه:

باستخدام system.collections.generic ؛ Class Foo {} Class Bar: Foo {}

static class Program
{
    static IEnumerator<Foo> GetBase() {
        yield return new Foo();
        yield return new Bar();
    }
    static IEnumerator<Bar> GetDerived()
    {
        return (IEnumerator<Bar>)GetBase();
    }
    static void Main()
    {
        var obj = GetDerived(); // EXCEPTION
    }
}

ومع ذلك ، يجب أن تكون قادرًا على استخدام كتلة التكرار للقيام بممثلي لك؟

static IEnumerator<Bar> GetDerived()
{
    using (IEnumerator<Foo> e = GetBase())
    {
        while (e.MoveNext())
        {
            // or use "as" and only return valid data
            yield return (Bar)e.Current;
        }
    }
}

للسبب لماذا هذا غير مناسب ، صورة بدلاً من Enumerator, ، أ List. كلاهما يستخدم الأدوية - لا يتعامل المحول البرمجي إما بطريقة خاصة فيما يتعلق بالوسائط العامة.

void doStuff() {
    List<IParentThing> list = getList();
    list.add(new ChildThing2());
}

List<IParentThing> getList() {
    return new List<ChildThing1>();  //ERROR!
}

هذه الطريقة الأولى جيدة - قائمة من IParentThingيجب أن تكون قادرة على تلقي أ ChildThing2. لكن قائمة ChildThing1S لا يمكن التعامل مع أ ChildThing2, ، أو في الواقع أي تطبيق IParentThing غير ذلك ChildThing1 - بمعنى آخر ، إذا List&lt;ChildThing1> سمح أن يلقي بصفته أ List&lt;IParent>, ، يجب أن تكون قادرة على التعامل معها الكل فئات فرعية من IParentThing, ، ليس مجرد IParentThing و ChildThing1.

لاحظ أن Java Generics لديها طريقة للقول إن "أريد قائمة من أي شيء يرث من هذا" بالإضافة إلى "أريد قائمة بأي شيء يرثه هذا" ، والذي يسمح بمزيد من الحلول إثارة (وفي رأيي الأنيق) لبعض المشاكل.

IEnumerator<BaseClass> و IEnumerator<ParentClass> لا علاقة لها ، على الرغم من أن معلماتها العامة هي. أود بدلاً من ذلك استخدام linq Select طريقة التمديد مثل SO:

return this.Select(x => (IParentClass)x).GetEnumerator();

أو ال Cast طريقة التمديد:

return this.Cast<IParentClass>().GetEnumerator();
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top