Delegates
1. إيه هو الـ Delegate؟
زي “مندوب”: تخيل إنك مدير في شركة، وعايز تدي مهمة لموظف. ممكن تختار أي موظف يعمل المهمة دي من غير ما تعرف اسمه، المهم يكون عنده نفس الشروط (مثلًا: خبرة في مجال معين).
معناها التفويض (إني بفوض حد عشان يعملي حاجة معينة)
بالبرمجة: الـ Delegate هو نوع بيشير لـ Method معين، وبيسمحلك تستدعي أي Method من خلاله طالما “شكلهم” واحد (نفس عدد البارامترات ونوع الـ return type).
2. ليه نستخدم Delegates؟
- تجنب الـ if-else الكتير: زي مثال الآلة الحاسبة اللي فيها (+ , - , * , /). بدل ما تعمل شروط كتير، تقدر تاخد العملية الحسابية كـ parameter.
- المرونة: تقدر تضيف عمليات جديدة من غير ما تغير في الكود الأساسي.
- الاستخدام في (Events): أغلب أنظمة الـ UI (مثل زر عند الضغط عليه) بتستخدم Delegates.
3. طريقة كتابة Delegate خطوة بخطوة
الآلة الحاسبة من غير Delegate
static void Main(string[] args)
{
int num1 = 10;
int num2 = 2;
Calculator(num1, num2, '+'); // 12
Calculator(num1, num2, '-'); // 8
Calculator(num1, num2, '*'); // 20
Calculator(num1, num2, '/'); // 5
//Calculator(num1, num2, '%');
}
static void Calculator(int a, int b, char op)
{
int result = 0;
if (op == '+')
result = a + b;
else if (op == '-')
result = a - b;
else if (op == '*')
result = a * b;
else if (op == '/')
result = a / b;
else
throw new ArgumentException("Invalid Operator");
Console.WriteLine(result);
}
الخطوة 1: تعريف الـ Delegate
- كده زي ما تحدد “شكل” الميثود اللي هتقبلها.
// ده شكل الميثود اللي هتقبلها: أي دالة بتاخد رقمين وترجع رقم
public delegate int CalculatorDelegate(int a, int b);
الخطوة 2: إنشاء دوال بتناسب الشكل ده
public static int Add(int a, int b) => a + b;
public static int Subtract(int a, int b) => a - b;
الخطوة 3: استخدام الـ Delegate
CalculatorDelegate del = Add; // Delegete point to Add
int result = del(10, 5); // 15
// Make delegate point to Subtract
del = Subtract;
result = del(10, 5); // 5
4. مثال الآلة الحاسبة بدون if-else
static void Main()
{
Calculator(10, 5, Add); // 15
Calculator(10, 5, Subtract); // 5
Calculator(10, 5, (a, b) => a * b); // لامبدا: 50
}
static void Calculator(int a, int b, CalculatorDelegate operation)
{
int result = operation(a, b);
Console.WriteLine(result);
}
5. حاجات مهمة
أ. الـ Multicast Delegate
- تقدر تخلي المندوب يشير لأكتر من ميثود في نفس الوقت.
CalculatorDelegate del = Add;
del += Subtract; // هينفذ الجمع والطرح بالترتيب
del(10, 5); // 15 From add then 5 From subtract
ب. الـ Lambda Expressions
- طريقة مختصرة لكتابة الميثود الصغيرة من غير ما تعرفها بشكل منفصل.
- نقدر بدل ما نمرر Method للـ Delegates اننا نمرر Lambda Expression
Calculator(10, 5, (a, b) => a / b); // Divide: 2
Calculator(10, 5, delegate(int a, int b) { return a % b; }); // 0
6. مثال واقعي: فلترة الموظفين
public delegate bool FilterDelegate(Employee emp);
void PrintFilteredEmployees(List<Employee> employees, FilterDelegate filter)
{
foreach (var emp in employees)
{
if (filter(emp)) // لو الشرط اتحقق
Console.WriteLine(emp.Name);
}
}
// Using:
PrintFilteredEmployees(employees, emp => emp.Salary > 5000);
// الموظفين اللي مرتبهم فوق 5000
Generic Delegates
- اتكلمنا عن الـ Generics قبل كدا ومعناها ممكن تراجعها.
1. What is Generic Delegates
الـ (Generic Delegates) هي تحسين لـTraditional Generics تتيح تعريف Delegates بنوع عام (Generic Type)، مما يجعلها:
- أكثر مرونة: تعمل مع أنواع بيانات مختلفة دون الحاجة لإعادة التعريف
- أكثر أمانًا: تضمان تطابق أنواع المعاملات مع التوقعات
- أقل تكرارًا: تقلل الحاجة لتعريف مفوضات متعددة لنفس البنية
2. الأنواع الجاهزة:
بتوفر سي شارب ثلاثة Generic Delegates جاهزة:
a. Action<T>
- الاستخدام: لـ Methods مش بتعمل Return بتبقا (void)
- أمثلة:
Action<string> printAction = s => Console.WriteLine(s);
printAction("Hello Generics!"); // Hello Generics!
b. Func<T, TResult>
- الاستخدام: لـ Methods بتعمل Return
- مثال:
Func<int, int, int> add = (a, b) => a + b;
int result = add(5, 3); // 8
c. Predicate<T>
- الاستخدام: لـ Methods بتعمل Return لـ Bool زي (true/false)
- مثال:
Predicate<int> isEven = num => num % 2 == 0;
bool test = isEven(4); // true
3. Integration with Lists (List<T>
)
تُستخدم Generic Delegates بكثرة مع عمليات Lists، خاصة مع الـLINQ:
List<int> numbers = new List<int> {1, 2, 3, 4, 5};
// Action with ForEach
numbers.ForEach(n => Console.Write(n + " ")); // 1 2 3 4 5
// Predicate with FindAll
var evens = numbers.FindAll(n => n % 2 == 0); // [2, 4]
// Func with Select
var squares = numbers.Select(n => n * n); // [1, 4, 9, 16, 25]
4. Make your Own:
يمكن تعريف Generic Delegates خاصة بك:
public delegate TOutput MyConverter<TInput, TOutput>(TInput input);
// Using:
MyConverter<int, string> intToString = num => num.ToString();
string result = intToString(42); // "42"
5. مزايا الاستخدام:
- إعادة الاستخدام: نفس Delegate تعمل مع أنواع مختلفة.
- سلامة النوع: تحدد أنواع المعاملات في Runtime.
- التكامل مع Lambda Expressions: تبسيط كتابة الأكواد.
6. إزاى تتفادى الأخطاء الشائعة؟
- الـNull Reference Exception: تأكد أن الـ Delegate مش null قبل الاستدعاء.
- الـ(Signature): لازم شكل الميثود يطابق الـ Delegate (عدد البارامترات ونوعها ونوع الـ return).
- الـ Memory Leaks: لو استخدمت Delegates مع (Events)، متنساش تسيبهم بعد ما تخلص عشان متخليش الذاكرة تتهدر.
7. الفرق بين Delegate و Interface
- الـ Delegate: مناسب للمهام البسيطة اللي مش محتاجة كتير من الـ Methods.
- الـ Interface: أنسب لو عندك مجموعة من الـ Methods المترابطة وهو عبارة عن عقد.
الخلاصة
الـ Delegate هو أداة قوية في C# بتخلي الكود:
- أكثر مرونة: تقدر تغير السلوك من غير ما تعدل الكود الأساسي.
- أنظف: تتخلص من الـ if-else والـ switch الكتير.
- أقوى: مع الممارسة هتلاقي استخداماته في الأحداث والـ LINQ والـ async/await.