فهم التكرار في C#: استخدام IEnumerator<T> في حلقات foreach

في C#، يُعتبر التعاون مع المجموعات والتكرار عبر البيانات ممارسة أساسية للمطورين. تثار تساؤلات شائعة عند محاولة إعادة IEnumerator<T> من طريقة واستخدامه في حلقة foreach. تصبح هذه المسألة أكثر إثارة للاهتمام عندما تحتاج إلى التنقل عبر عناصر التحكم المتداخلة داخل واجهة المستخدم.

لنغوص في هذه الحالة من خلال استكشاف جوهر المشكلة وتقديم حل عملي.

المشكلة

تخيل أن لديك نموذج Windows حيث تحتاج إلى تعديل ارتفاع عناصر التحكم من نوع TextBox ديناميكيًا. قد لا تتواجد هذه TextBoxes فقط في المستوى الجذري للنموذج، ولكن قد يكون بعضها مدمجًا داخل عناصر تحكم أخرى، مما يخلق هيكلًا معقدًا. تريد استخدام طريقة تستخدم yield لإعادة TextBoxes التي تم العثور عليها واحدة تلو الأخرى.

قد تبدو طريقة البدء الخاصة بك على النحو التالي:

private static IEnumerator<TextBox> FindTextBoxes(Control rootControl)
{
    foreach (Control control in rootControl.Controls)
    {
        if (control.Controls.Count > 0)
        {
            // البحث بشكل تكراري عن أي نصوص داخل كل عنصر تحكم فرعي
            foreach (TextBox textBox in FindTextBoxes(control))
            {
                yield return textBox;
            }
        }

        TextBox textBox2 = control as TextBox;
        if (textBox2 != null)
        {
            yield return textBox2;
        }
    }
}

ومع ذلك، عند استخدام هذه الطريقة داخل حلقة foreach كما يلي:

foreach(TextBox textBox in FindTextBoxes(this))
{
    textBox.Height = height;
}

ستواجه خطأً في المترجم. يحدث هذا الخطأ لأن حلقة foreach تتوقع IEnumerable، لكن طريقتك تعيد IEnumerator.

الحل

تكمن المفتاح لحل هذه المشكلة في تغيير نوع العائد لطريقتك من IEnumerator<T> إلى IEnumerable<T>. يسمح لك بناء الجملة yield return في C# بإنشاء مُكرر بشكل فعّال بدون الحاجة إلى فئة مُعَدٍ منفصلة.

إليك كيفية تعديل طريقتك:

الطريقة المعدلة

private static IEnumerable<TextBox> FindTextBoxes(Control rootControl)
{
    foreach (Control control in rootControl.Controls)
    {
        // التحقق إذا كان هناك أية عناصر تحكم فرعية
        if (control.Controls.Count > 0)
        {
            // البحث بشكل تكراري عن TextBoxes في عناصر التحكم الفرعية
            foreach (TextBox textBox in FindTextBoxes(control))
            {
                yield return textBox;
            }
        }

        // إذا كان عنصر التحكم الحالي هو TextBox، قم بإعادته
        if (control is TextBox textBox2)
        {
            yield return textBox2;
        }
    }
}

الشرح

  1. تغيير نوع العائد: تعيد الطريقة الآن IEnumerable<TextBox> بدلاً من IEnumerator<TextBox>. يعني هذا أنه يمكن استخدامها مباشرة في حلقة foreach.

  2. استخدام yield return: يوفر استخدام yield return وسيلة نظيفة وفعّالة لاسترجاع عناصر التحكم من نوع TextBox أثناء تقدم الحلقة.

مع هذا التغيير، سيعمل استخدام الطريقة في حلقة foreach بشكل سلس الآن، وسيتم معالجة كل TextBox بشكل فردي.

الخاتمة

قد يؤدي العمل مع مجموعات C# وحلقات foreach في بعض الأحيان إلى تحديات غير متوقعة، خاصة عند التعامل مع طرق استدعاء تكرارية وهياكل التحكم. عن طريق تغيير نوع العائد من IEnumerator<T> إلى IEnumerable<T>, يمكنك الاستفادة بسهولة من التعداد في تراكيب الحلقات دون التعقيدات الإضافية.

باتباع هذا النهج، لن تتجنب الأخطاء في المترجم فحسب، بل ستعزز أيضًا إمكانية صيانة وقراءة الكود الخاص بك عند إدارة عناصر التحكم المتداخلة في النموذج الخاص بك.

للمزيد من القراءة، تعرف على مفاهيم مثل IEnumerable، IEnumerator، وكلمة yield لاستغلال إمكانيات التكرار في C# بالكامل.