در زبان C#، توابع هم داده هستند.
یعنی میتوانیم آنها را به متغیرها نسبت دهیم، به عنوان پارامتر به متدها ارسال کنیم، یا حتی در لیستها ذخیره کنیم.
پایهی این قابلیت در C#، مفهومی است به نام Delegate.
Delegateها به همراه Func<> و Action<> یکی از مهمترین ابزارهای برنامهنویسی مدرن در داتنت محسوب میشوند.
در این مقاله بهصورت مفهومی و کاربردی یاد میگیریم Delegate چیست، Func چه تفاوتی دارد، و در چه موقعیتهایی استفاده میشوند.
به زبان ساده، Delegate نوعی متغیر است که میتواند به یک تابع اشاره کند.
در واقع اشارهگر به تابع (Function Pointer) در داتنت است.
با استفاده از Delegate میتوانیم تابعی را:
در یک متغیر نگه داریم
به متد دیگر ارسال کنیم
یا در زمان اجرا تصمیم بگیریم کدام تابع اجرا شود
// تعریف delegate delegate void MyDelegate(); // تابعی که با آن سازگار است void SayHello() { Console.WriteLine("Hello!"); } // استفاده از delegate MyDelegate d = SayHello; d(); // خروجی: Hello!
در این مثال:
MyDelegate نوعی delegate است که به هر تابعی با امضای void() اشاره میکند.
متغیر d اکنون تابع SayHello را در خود نگه میدارد.
با اجرای d() در واقع همان تابع SayHello فراخوانی میشود.
Delegateها میتوانند پارامتر ورودی و مقدار خروجی هم داشته باشند:
delegate int MathOperation(int a, int b); int Add(int x, int y) => x + y; int Multiply(int x, int y) => x * y; void RunOperation(MathOperation op) { Console.WriteLine(op(3, 4)); } RunOperation(Add); // 7 RunOperation(Multiply); // 12
در این مثال، تابع RunOperation خودش تابعی از نوع MathOperation را به عنوان پارامتر دریافت میکند.
این دقیقاً همان مفهوم ارسال تابع به تابع دیگر (Higher-Order Function) است.
در C# برای راحتتر نوشتن delegateها، از Lambda Expression استفاده میشود.
Lambda یعنی تابع بینام:
MathOperation subtract = (a, b) => a - b; Console.WriteLine(subtract(10, 3)); // خروجی: 7
Func<> در واقع یک نوع Delegate آماده است که در خود داتنت تعریف شده.
نیازی نیست هر بار delegate جدید بنویسی.
قانون کلی:
Funcهمیشه خروجی دارد (Return Value).
Func<T1, T2, ..., TResult>
تمام پارامترهای اولی (T1, T2, ...) ورودی تابع هستند
آخرین پارامتر (TResult) خروجی تابع است
Func<int> getNumber = () => 5; Console.WriteLine(getNumber()); // 5 Func<int, int> square = x => x * x; Console.WriteLine(square(4)); // 16 Func<int, int, int> add = (a, b) => a + b; Console.WriteLine(add(3, 7)); // 10
در همهٔ این مثالها، ما تابع را در قالب یک متغیر از نوع Func ذخیره کردهایم.
اگر تابع خروجی ندارد، از Action استفاده میکنیم:
نوعخروجی دارد؟مثالFunc<int, int>✅ دارد (int برمیگرداند)x => x * 2Action<string>❌ ندارد (void است)x => Console.WriteLine(x)
در سیستمهای مدرن (مثلاً ASP.NET Core)، از Func و delegate برای طراحی صف کار (Background Queue) استفاده میشود.
مثلاً متد زیر در یک صف کاری وجود دارد:
void Enqueue(Func<CancellationToken, Task> workItem);
یعنی:
تابعی را بفرست که یک
CancellationTokenمیگیرد و یکTaskبرمیگرداند (یعنی کاری async انجام میدهد).
کاربردش چیست؟
ارسال کارهایی مثل ارسال ایمیل، ثبت گزارش، یا Sync با APIها به صفی که در پسزمینه اجرا میشود.
مثال:
queue.Enqueue(async token => { await Task.Delay(2000, token); Console.WriteLine("Job done!"); });
✅ جدا کردن منطق از ساختار
✅ افزایش انعطاف در طراحی (قابلیت پاس دادن رفتار به متدها)
✅ کاهش تکرار کد
✅ پشتیبانی از الگوهای تابعی (Functional Patterns)
✅ کاربرد گسترده در LINQ، async/await، و eventها
مفهومتعریفمثالDelegateنوع دادهای که به یک تابع اشاره میکندdelegate void MyDelegate();FuncDelegate آماده برای توابع دارای خروجیFunc<int, int, int> add = (a,b)=>a+b;ActionDelegate آماده برای توابع بدون خروجیAction<string> show = s => Console.WriteLine(s);
Delegate پایهی همهی الگوهای callback، event، async task، LINQ و middleware در داتنت است.
Func<>وAction<>فقط نسخههای راحتتر و از پیشتعریفشدهٔ آن هستند.