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.