البرمجة الوظيفية في لغة C#: من النظرية إلى التطبيق
البرمجة الوظيفية (FP) هي نموذج يتم فيه التعامل مع العمليات الحسابية كتقييم للدوال الرياضية، مع تجنب تغيير الحالة والبيانات القابلة للتغيير. على الرغم من أن لغة C# كانت في الأصل لغة موجهة للكائنات، إلا أنها منذ الإصدار 3.0 بدأت في دمج الميزات الوظيفية بنشاط. اليوم، تدعم C# مفاهيم مثل تعبيرات لامدا، LINQ، الأنواع غير القابلة للتغيير، وحتى مطابقة الأنماط. في هذه المقالة، سنقوم بتفصيل المبادئ الأساسية للبرمجة الوظيفية ونوضح كيفية تطبيقها في مشاريع C# الواقعية.
1. الدوال النقية وغياب الآثار الجانبية
الدالة النقية هي دالة:
- تعيد دائمًا نفس النتيجة لنفس الوسائط (الشفافية المرجعية).
- ليس لها آثار جانبية (لا تعدل الحالة العامة، أو تكتب في الملفات، أو ترسل طلبات HTTP).
في لغة C#، قد تبدو الدالة النقية كما يلي:
public static int Add(int a, int b) => a + b;إليك مثال على دالة غير نقية:
public static int AddAndLog(int a, int b){ Console.WriteLine($"Adding {a} and {b}"); // تأثير جانبي return a + b;}مزايا الدوال النقية: يسهل اختبارها وتصحيح أخطائها وتنفيذها بالتوازي.
2. عدم القابلية للتغيير
في البرمجة الوظيفية، البيانات غير قابلة للتغيير افتراضيًا. في C#، يتم تحقيق ذلك باستخدام:
- الكلمة المفتاحية
readonlyللحقول. - هياكل
record(C# 9+) التي تنشئ تلقائيًا كائنات غير قابلة للتغيير. - المجموعات من مساحة الأسماء
System.Collections.Immutable.
مثال مع record:
public record Person(string Name, int Age);إذا كنت بحاجة إلى تغيير العمر، يتم إنشاء مثيل جديد:
Person person = new("Ivan", 30);Person olderPerson = person with { Age = 31 };عدم القابلية للتغيير يمنع التعديلات العرضية ويبسط تعدد المهام.
3. الدوال عالية المستوى وتعبيرات لامدا
الدوال عالية المستوى تأخذ دوال أخرى كوسائط أو تعيدها. في C#، يتم تنفيذ ذلك من خلال المفوضين: Func<>، Action<>، Predicate<>.
مثال بسيط هو طريقة Where من LINQ:
List<int> numbers = new() { 1, 2, 3, 4, 5 };var evenNumbers = numbers.Where(n => n % 2 == 0).ToList();هنا، n => n % 2 == 0 هو تعبير لامدا (دالة مجهولة).
يمكنك كتابة دالة عالية المستوى خاصة بك:
public static List<T> Filter<T>(List<T> list, Func<T, bool> predicate){ var result = new List<T>(); foreach (var item in list) if (predicate(item)) result.Add(item); return result;}4. LINQ: أداة قوية بأسلوب وظيفي
LINQ (استعلام متكامل مع اللغة) هو ربما المثال الأبرز للبرمجة الوظيفية في C#. يسمح لك بمعالجة المجموعات بشكل تصريحي، مشابه لـ SQL.
مثال: الحصول على أسماء المستخدمين الأكبر من 18 عامًا، مرتبة حسب العمر:
var users = new List<User>{ new("Anna", 22), new("Boris", 17), new("Victor", 30)};
var result = users .Where(u => u.Age >= 18) .OrderBy(u => u.Age) .Select(u => u.Name) .ToList();يدعم LINQ التقييم البطيء، مما يوفر الموارد.
5. المونادات ونوع Maybe في C#
المونادات هي تجريد لسلسلة العمليات الحسابية مع سياق (مثل التعامل مع القيمة الفارغة أو التزامن). لا تحتوي C# على موناد Maybe مدمج، ولكن من السهل تنفيذه:
public class Maybe<T>{ private readonly T _value; public bool HasValue { get; } p