0Pricing
C# Academy · درس

الأنواع القابلة للتعداد المخصّصة

نفّذوا IEnumerable باستخدام التكرارات

الأنواع القابلة للتعداد المخصّصة درس مجاني في C# Academy على CoddyKit. هذا هو الدرس 4 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في C# Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة C# Academy 4 دروس في المجموع.

إنشاء قابل للتعداد خاص بك

بتنفيذ IEnumerable<T> في فئة، تجعلها قابلة للاستخدام في foreach. وبدمج ذلك مع أساليب المكرّرات، يمكنك إنشاء مجموعات ونطاقات مخصصة بقدر قليل جدًا من التعليمات البرمجية.

تنفيذ GetEnumerator باستخدام yield

أبسط طريقة لتنفيذ IEnumerable<T> هي كتابة GetEnumerator كمكرّر باستخدام yield return. وينشئ المصرّف كائن التعداد نيابةً عنك.

using System;
using System.Collections;
using System.Collections.Generic;

public class IntRange : IEnumerable<int>
{
    private readonly int _start, _count;
    public IntRange(int start, int count) { _start = start; _count = count; }

    public IEnumerator<int> GetEnumerator()
    {
        for (int i = 0; i < _count; i++)
            yield return _start + i;
    }
    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}

public class Program
{
    public static void Main()
    {
        foreach (var n in new IntRange(5, 4))
            Console.WriteLine(n);
    }
}

لماذا يوجد أسلوبا GetEnumerator؟

ترث IEnumerable<T> الواجهة غير العامة IEnumerable، لذلك يجب تنفيذ الواجهتين. أما النسخة غير العامة الصريحة فتفوّض ببساطة إلى النسخة العامة.

using System;
using System.Collections;
using System.Collections.Generic;

public class Letters : IEnumerable<char>
{
    public IEnumerator<char> GetEnumerator()
    {
        yield return 'x';
        yield return 'y';
        yield return 'z';
    }
    // Required because IEnumerable<T> extends IEnumerable
    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}

public class Program
{
    public static void Main()
    {
        foreach (var c in new Letters())
            Console.WriteLine(c);
    }
}

تغليف المنطق المخصص

يمكن لقابل تعداد مخصص إخفاء قواعد توليد معقدة خلف نوع واضح. تنشئ الفئة هنا تسلسل فيبوناتشي بطول تختاره.

using System;
using System.Collections;
using System.Collections.Generic;

public class Fibonacci : IEnumerable<int>
{
    private readonly int _count;
    public Fibonacci(int count) { _count = count; }

    public IEnumerator<int> GetEnumerator()
    {
        int a = 0, b = 1;
        for (int i = 0; i < _count; i++)
        {
            yield return a;
            int next = a + b; a = b; b = next;
        }
    }
    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}

public class Program
{
    public static void Main()
    {
        foreach (var n in new Fibonacci(8))
            Console.WriteLine(n);
    }
}

تغليف مجموعة داخلية

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

using System;
using System.Collections;
using System.Collections.Generic;

public class EvenBag : IEnumerable<int>
{
    private readonly List<int> _items = new List<int>();
    public void Add(int n) { if (n % 2 == 0) _items.Add(n); }

    public IEnumerator<int> GetEnumerator() => _items.GetEnumerator();
    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}

public class Program
{
    public static void Main()
    {
        var bag = new EvenBag();
        bag.Add(1); bag.Add(2); bag.Add(3); bag.Add(4);
        foreach (var n in bag) Console.WriteLine(n);
    }
}

القوابل المخصصة للتعداد متوافقة مع LINQ

يحصل أي نوع ينفّذ IEnumerable<T> فورًا على جميع إمكانات LINQ. ويمكن تصفية مجموعتك المخصصة وإسقاطها وتجميعها دون جهد إضافي.

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;

public class IntRange : IEnumerable<int>
{
    private readonly int _start, _count;
    public IntRange(int start, int count) { _start = start; _count = count; }
    public IEnumerator<int> GetEnumerator()
    {
        for (int i = 0; i < _count; i++) yield return _start + i;
    }
    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}

public class Program
{
    public static void Main()
    {
        var range = new IntRange(1, 10);
        Console.WriteLine(range.Where(n => n % 3 == 0).Sum());
    }
}

يحصل كل foreach على معدِّد جديد

بما أن GetEnumerator يُستدعى في كل مرة تكرّر فيها، يمكن تعداد قابل التعداد المخصص مرارًا، مع بدء كل مرور من جديد.

using System;
using System.Collections;
using System.Collections.Generic;

public class IntRange : IEnumerable<int>
{
    private readonly int _count;
    public IntRange(int count) { _count = count; }
    public IEnumerator<int> GetEnumerator()
    {
        for (int i = 1; i <= _count; i++) yield return i;
    }
    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}

public class Program
{
    public static void Main()
    {
        var r = new IntRange(3);
        foreach (var n in r) Console.Write(n);
        Console.WriteLine();
        foreach (var n in r) Console.Write(n); // fresh again
        Console.WriteLine();
    }
}

قابل تعداد مخصص باستخدام الأنواع العامة

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

using System;
using System.Collections;
using System.Collections.Generic;

public class Ring<T> : IEnumerable<T>
{
    private readonly T[] _items;
    private readonly int _start;
    public Ring(T[] items, int start) { _items = items; _start = start; }

    public IEnumerator<T> GetEnumerator()
    {
        for (int i = 0; i < _items.Length; i++)
            yield return _items[(_start + i) % _items.Length];
    }
    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}

public class Program
{
    public static void Main()
    {
        var ring = new Ring<string>(new[] { "a", "b", "c", "d" }, 2);
        foreach (var s in ring) Console.WriteLine(s);
    }
}

قوابل تعداد مخصصة كسولة

بما أن GetEnumerator هو نفسه مكرّر، يكون قابل التعداد المخصص كسولًا افتراضيًا: لا تُنتَج العناصر إلا عندما يسحبها المستهلك.

using System;
using System.Collections;
using System.Collections.Generic;

public class Squares : IEnumerable<int>
{
    private readonly int _count;
    public Squares(int count) { _count = count; }
    public IEnumerator<int> GetEnumerator()
    {
        for (int i = 1; i <= _count; i++)
        {
            Console.WriteLine("computing " + i);
            yield return i * i;
        }
    }
    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}

public class Program
{
    public static void Main()
    {
        foreach (var n in new Squares(3))
            Console.WriteLine("got " + n);
    }
}

متى تنشئ قابل تعداد مخصصًا

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

using System;
using System.Collections;
using System.Collections.Generic;

public class Countdown : IEnumerable<int>
{
    private readonly int _from;
    public Countdown(int from) { _from = from; }
    public IEnumerator<int> GetEnumerator()
    {
        for (int i = _from; i >= 0; i--) yield return i;
    }
    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}

public class Program
{
    public static void Main()
    {
        foreach (var n in new Countdown(5)) Console.WriteLine(n);
    }
}

جرّب بنفسك

أنشئ قابل تعداد مخصصًا ينتج تسلسلًا هندسيًا، ثم استخدم LINQ عليه. وسيتكامل نوعك مباشرةً مع النظام البيئي بأكمله.

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;

public class Geometric : IEnumerable<int>
{
    private readonly int _start, _ratio, _count;
    public Geometric(int start, int ratio, int count) { _start = start; _ratio = ratio; _count = count; }
    public IEnumerator<int> GetEnumerator()
    {
        int value = _start;
        for (int i = 0; i < _count; i++) { yield return value; value *= _ratio; }
    }
    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}

public class Program
{
    public static void Main()
    {
        var seq = new Geometric(1, 2, 6);
        Console.WriteLine(string.Join(", ", seq));
        Console.WriteLine("sum: " + seq.Sum());
    }
}

تحقق سريع

تذكّر عقد IEnumerable.

مراجعة

ينفّذ قابل التعداد المخصص IEnumerable<T> لكي يعمل مع foreach وLINQ.

  • نفّذ GetEnumerator كمكرّر باستخدام yield return.
  • وفّر أيضًا النسخة غير العامة الصريحة من GetEnumerator.
  • يحصل كل تكرار على معدِّد جديد.
  • يكون التكرار كسولًا افتراضيًا.

الأسئلة الشائعة

هل درس «الأنواع القابلة للتعداد المخصّصة» مجاني؟

نعم — نص درس «الأنواع القابلة للتعداد المخصّصة» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة C# Academy، انتقل إلى CoddyKit PRO. تتضمن دورة C# Academy 4 دروس في المجموع.

ماذا ستتعلم في «الأنواع القابلة للتعداد المخصّصة»؟

نفّذوا IEnumerable باستخدام التكرارات تتمرن على C# Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.

هل أحتاج إلى خبرة سابقة لأبدأ C# Academy؟

لا تُشترط خبرة سابقة. C# Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 4 من أصل 4.

كم من الوقت يستغرق درس «الأنواع القابلة للتعداد المخصّصة»؟

معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.

هل يمكنني كتابة وتشغيل أكواد في درس C# Academy هذا؟

نعم. كل درس في C# Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.

جميع الدروس في هذه الدورة

  1. أساليب التكرار باستخدام yield return
  2. ‏yield break والإنهاء المبكر
  3. دلالات التقييم الكسول
  4. الأنواع القابلة للتعداد المخصّصة
← العودة إلى C# Academy