هل من الخطأ إلقاء تعداد لفئة الطفل على تعداد فئة الوالدين؟
-
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
. لكن قائمة ChildThing1
S لا يمكن التعامل مع أ ChildThing2
, ، أو في الواقع أي تطبيق IParentThing
غير ذلك ChildThing1
- بمعنى آخر ، إذا List<ChildThing1>
سمح أن يلقي بصفته أ List<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();