كيفية إزالة الكائنات من مجموعة لا تعد ولا تحصى في حلقة [مكررة]
-
03-07-2019 - |
سؤال
هذا السؤال لديه بالفعل إجابة هنا:
ينسخ
تعديل مجموعة أثناء التكرار من خلالها
هل لدى أي شخص نمط لطيف يسمح لي بالتغلب على عدم القدرة على إزالة الكائنات أثناء التنقل عبر مجموعة لا تعد ولا تحصى (على سبيل المثال، IList أو KeyValuePairs في القاموس)
على سبيل المثال، يفشل ما يلي، لأنه يعدل القائمة التي يتم تعدادها أثناء عملية البحث
foreach (MyObject myObject in MyListOfMyObjects)
{
if (condition) MyListOfMyObjects.Remove(myObject);
}
لقد استخدمت طريقتين في الماضي.
لقد استبدلت foreach بحلقة for معكوسة (حتى لا أغير أي فهارس أقوم بتكرارها إذا قمت بإزالة كائن).
لقد حاولت أيضًا تخزين مجموعة جديدة من الكائنات لإزالتها داخل التكرار، ثم تكرار هذه المجموعة وإزالة الكائنات من المجموعة الأصلية.
هذه تعمل بشكل جيد، ولكن لا يشعر جميل، وكنت أتساءل عما إذا كان أي شخص قد توصل إلى المزيد رائع حل لهذه القضية
المحلول
هناك مفيد List<T>.RemoveAll(Predicate<T> match)
الطريقة التي أعتقد أنها مصممة لهذا: http://msdn.microsoft.com/en-us/library/wdka673a.aspx
نصائح أخرى
إنه أمر بسيط نوعًا ما، ولكن عندما أخطط لحذف عناصر من IEnumerable/IList، عادةً ما أقوم بعمل نسخة:
foreach (MyObject myObject in new List<MyObject>(MyListOfMyObjects))
{
if (condition) MyListOfMyObjects.Remove(myObject);
}
إنها ليست الطريقة الأكثر فعالية للقيام بذلك، ولكن من السهل قراءتها.التحسين المبكر وكل ذلك.
قم بالعكس، وقم بإنشاء قائمة جديدة:
List myFilteredList = new List();
foreach (MyObject myObject in myListOfMyObjects)
{
if (!condition) myFilteredList.Add(myObject);
}
ثم استخدم القائمة الجديدة أينما كنت في حاجة إليها.
يمكنك أيضًا استخدام تعبير LINQ بسهولة، مرة أخرى، لعكس الشرط.وهذا له فائدة إضافية تتمثل في عدم إنشاء بنية جديدة، ولكن أيضًا هناك عيوب في كونها قابلة للتعداد كسولًا:
var myFilteredList = from myObject in myListOfMyObjects
where !condition
select myObject;
ومع ذلك، إذا كنت تريد حقًا إزالة العناصر من القائمة، فعادةً ما أستخدم أسلوب "إنشاء قائمة جديدة، ثم التكرار والإزالة".
لقد عثرت للتو على هذا المنشور واعتقدت أنني سأشاركه.
void RemoveAll(object condition)
{
bool found = false;
foreach(object thisObject in objects)
{
if (condition)
{
objects.Remove(thisObject);
found = true;
break; //exit loop
}
}
// Call again recursively
if (found) RemoveAll(condition);
}
لا أحب فكرة الحلقة المعكوسة، نظرًا لأن ذلك يعمل فقط على هياكل بيانات معينة.
بشكل عام، سأستخدم التقنية الثانية وأقوم بتجميع العناصر المراد حذفها في مجموعة منفصلة "سيتم حذفها".إذا كان الحذف يمكن أن يتسبب في إبطال التكرارات الحالية (كما سيحدث مع أي مجموعة شجرية متوازنة على سبيل المثال)، فأنا لا أرى طريقة للتغلب على هذا الأمر.
الأسلوب الآخر الوحيد الذي استخدمته أحيانًا هو إعادة تشغيل التكرار بالكامل عندما تجد العنصر الأول الذي تريد حذفه.إذا قمت بذلك دون العثور على أي عناصر لحذفها، فستنتهي الوظيفة.هذا غير فعال، ولكنه ضروري في بعض الأحيان إذا كان حذف عنصر واحد من المجموعة قد يؤدي إلى تغيير مجموعة العناصر التي يجب حذفها.
لدي قاموس وأريد التخلص من كل القيم.عندما يتم التخلص من كل قيمة، فإنها تزيل نفسها من القاموس مما يخلق المشكلة التي تناقشها.فعلت ما يلي:
foreach (var o in dictionary.Values.ToList())
{
o.Dispose();
}
أقدر أن هذا قد يكون ميتًا الآن ولكن الطريقة التي أفعل بها ذلك دائمًا هي:
foreach (MyObject myObject في MyListOfMyObjects)
{
إذا (حالة) MyListOfMyObjects.Remove(myObject);
استراحة؛
}
تتم إزالة الكائن ثم تخرج الحلقة، فيولا!