توسيع الواجهات والأنواع العامة
أضيفوا سلوكًا إلى أنواع عديدة دفعةً واحدة
توسيع الواجهات والأنواع العامة درس مجاني في C# Academy على CoddyKit. هذا هو الدرس 2 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في C# Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة C# Academy 4 دروس في المجموع.
توسيع الواجهات
يمكنكم كتابة أسلوب امتدادي يكون معامله this واجهة. وكل نوع يطبّق هذه الواجهة سيحصل على الأسلوب فورًا. وهذه هي الطريقة التي تضيف بها LINQ عشرات الأساليب إلى IEnumerable<T>.
امتداد عام على IEnumerable
بجعل الأسلوب عامًا بالنسبة إلى T وتوسيعه لـ IEnumerable<T>، تعمل الأداة المساعدة مع أي تسلسل: القوائم والمصفوفات ونتائج الاستعلامات وغيرها.
using System;
using System.Collections.Generic;
public static class EnumerableExtensions
{
public static bool IsEmpty<T>(this IEnumerable<T> source)
{
foreach (var item in source) return false;
return true;
}
}
public class Program
{
public static void Main()
{
var nums = new List<int> { 1, 2, 3 };
Console.WriteLine(nums.IsEmpty());
Console.WriteLine(new int[0].IsEmpty());
}
}تكرار التسلسل
داخل الامتداد، تتعاملون مع المعلمة مثل أي IEnumerable<T>. نحسب هنا مجموعًا من دون الاعتماد على LINQ.
using System;
using System.Collections.Generic;
public static class EnumerableExtensions
{
public static int SumValues(this IEnumerable<int> source)
{
int total = 0;
foreach (var n in source) total += n;
return total;
}
}
public class Program
{
public static void Main()
{
var nums = new List<int> { 10, 20, 30 };
Console.WriteLine(nums.SumValues());
}
}إرجاع تسلسل جديد
يمكن للامتدادات إرجاع التسلسلات أيضًا. وعند دمجها مع yield return، تصبح عوامل تشغيل كسولة على نمط LINQ.
using System;
using System.Collections.Generic;
public static class EnumerableExtensions
{
public static IEnumerable<T> EveryOther<T>(this IEnumerable<T> source)
{
bool take = true;
foreach (var item in source)
{
if (take) yield return item;
take = !take;
}
}
}
public class Program
{
public static void Main()
{
var letters = new List<string> { "a", "b", "c", "d", "e" };
foreach (var l in letters.EveryOther())
Console.WriteLine(l);
}
}قيود الأنواع العامة
يمكنكم تقييد المعلمة العامة لفرض امتلاكها لقدرات معينة. يتيح لنا where T : IComparable<T> هنا مقارنة العناصر للعثور على القيمة العظمى.
using System;
using System.Collections.Generic;
public static class EnumerableExtensions
{
public static T MaxItem<T>(this IEnumerable<T> source) where T : IComparable<T>
{
bool started = false;
T best = default;
foreach (var item in source)
{
if (!started || item.CompareTo(best) > 0) { best = item; started = true; }
}
return best;
}
}
public class Program
{
public static void Main()
{
var nums = new List<int> { 3, 9, 1, 7 };
Console.WriteLine(nums.MaxItem());
}
}تمديد واجهة مخصصة
تستفيد واجهاتكم الخاصة أيضًا. عرّفوا واجهة، ثم أضيفوا السلوك المشترك إليها عبر الامتدادات بدلًا من تكراره في كل نوع يطبّقها.
using System;
public interface INamed { string Name { get; } }
public class Dog : INamed { public string Name => "Rex"; }
public static class NamedExtensions
{
public static string Greeting(this INamed n) => "Hello, " + n.Name;
}
public class Program
{
public static void Main()
{
var d = new Dog();
Console.WriteLine(d.Greeting());
}
}معلمات أنواع متعددة
يمكن أن تكون الامتدادات عامة بالنسبة إلى عدة معلمات للأنواع. يساعد عامل الإسقاط هذا على تحويل تسلسل باستخدام دالة، بطريقة تشبه Select في LINQ.
using System;
using System.Collections.Generic;
public static class EnumerableExtensions
{
public static IEnumerable<TResult> Map<TSource, TResult>(
this IEnumerable<TSource> source, Func<TSource, TResult> f)
{
foreach (var item in source) yield return f(item);
}
}
public class Program
{
public static void Main()
{
var nums = new List<int> { 1, 2, 3 };
foreach (var s in nums.Map(n => "#" + n))
Console.WriteLine(s);
}
}الدمج مع LINQ المضمّن
تتعايش أساليب الامتداد الخاصة بكم مع LINQ. يمكنكم ربط عامل التشغيل المخصص بآخرين قياسيين ضمن مسار معالجة واحد بأسلوب سلس.
using System;
using System.Collections.Generic;
using System.Linq;
public static class EnumerableExtensions
{
public static IEnumerable<T> EveryOther<T>(this IEnumerable<T> source)
{
bool take = true;
foreach (var item in source) { if (take) yield return item; take = !take; }
}
}
public class Program
{
public static void Main()
{
var nums = Enumerable.Range(1, 10);
var result = nums.Where(n => n > 2).EveryOther().ToList();
Console.WriteLine(string.Join(", ", result));
}
}تمديد IEnumerable يصل إلى أنواع كثيرة
بما أن المصفوفات والقوائم والقواميس ومجموعات التجزئة ونتائج الاستعلام تطبّق جميعها IEnumerable<T>، فإن امتدادًا واحدًا يغطيها كلها دفعة واحدة.
using System;
using System.Collections.Generic;
public static class EnumerableExtensions
{
public static int CountItems<T>(this IEnumerable<T> source)
{
int n = 0;
foreach (var item in source) n++;
return n;
}
}
public class Program
{
public static void Main()
{
Console.WriteLine(new[] { 1, 2, 3 }.CountItems());
Console.WriteLine(new HashSet<string> { "a", "b" }.CountItems());
}
}لماذا يتوسع هذا النمط
تمنحكم امتدادات الواجهات أدوات مساعدة متعددة الأشكال من دون وراثة. اكتبوا المنطق مرة واحدة اعتمادًا على الواجهة، وسيحصل عليه كل نوع يطبّقها، حاليًا ومستقبلًا، مجانًا.
using System;
using System.Collections.Generic;
public static class EnumerableExtensions
{
public static string JoinWith<T>(this IEnumerable<T> source, string sep)
{
return string.Join(sep, source);
}
}
public class Program
{
public static void Main()
{
var nums = new List<int> { 1, 2, 3 };
Console.WriteLine(nums.JoinWith(" -> "));
}
}جرّبوا بأنفسكم
أنشئوا عامل تشغيل عامًّا صغيرًا على IEnumerable<T> واربطوه مع LINQ. لاحظوا كيف يعمل مع أي نوع من التسلسلات.
using System;
using System.Collections.Generic;
using System.Linq;
public static class EnumerableExtensions
{
public static IEnumerable<T> WithoutDuplicatesByKey<T, TKey>(
this IEnumerable<T> source, Func<T, TKey> keySelector)
{
var seen = new HashSet<TKey>();
foreach (var item in source)
if (seen.Add(keySelector(item))) yield return item;
}
}
public class Program
{
public static void Main()
{
var words = new[] { "apple", "avocado", "banana", "cherry", "blueberry" };
var firstPerLetter = words.WithoutDuplicatesByKey(w => w[0]);
Console.WriteLine(string.Join(", ", firstPerLetter));
}
}تحقق سريع
فكّروا في كيفية تعريف الامتدادات على نمط LINQ.
مراجعة
يمثّل تمديد الواجهات، وخاصة IEnumerable<T>، أساس البرمجة على نمط LINQ.
- اجعلوا الأسلوب عامًا واستخدموا الواجهة بوصفها نوع
this. - استخدموا
yield returnلإنشاء عوامل تشغيل كسولة. - طبّقوا قيود
whereلفرض قدرات مثلIComparable<T>. - يغطي امتداد واجهة واحد جميع الأنواع التي تطبّقها، بما في ذلك الأنواع المستقبلية.
الأسئلة الشائعة
هل درس «توسيع الواجهات والأنواع العامة» مجاني؟
نعم — نص درس «توسيع الواجهات والأنواع العامة» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة C# Academy، انتقل إلى CoddyKit PRO. تتضمن دورة C# Academy 4 دروس في المجموع.
ماذا ستتعلم في «توسيع الواجهات والأنواع العامة»؟
أضيفوا سلوكًا إلى أنواع عديدة دفعةً واحدة تتمرن على C# Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.
هل أحتاج إلى خبرة سابقة لأبدأ C# Academy؟
لا تُشترط خبرة سابقة. C# Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 2 من أصل 4.
كم من الوقت يستغرق درس «توسيع الواجهات والأنواع العامة»؟
معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.
هل يمكنني كتابة وتشغيل أكواد في درس C# Academy هذا؟
نعم. كل درس في C# Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.
جميع الدروس في هذه الدورة
- تعريف Extension Methods
- توسيع الواجهات والأنواع العامة
- حلّ Extension Methods
- تصميم امتدادات جيدة