خبرهای فارسی

اخبار موسیقی، اخبار آی تی، اخبار ورزشی، اخبار استخدام

خبرهای فارسی

اخبار موسیقی، اخبار آی تی، اخبار ورزشی، اخبار استخدام

Closure چیست؟

اگر برنامه نویس C# باشید حتما به یاد دارید که Anonymous Method ها یا متد های بی نام یکی از ویژگی هایی بود که در نسخه دوم سی شارپ معرفی شد و همینطور در نسخه سوم عبارت های Lambda شکل بهتری به متد های بی نام دادند. مسلما بسیاری از ما کدی یا کد هایی مشابه زیر نوشته ایم:

1
2
3
4
5
6
7
static void Main(string[] args)
{
    int x = 0;
    Action action = delegate { Console.WriteLine(x); };
    x = 1;
    action();
}

یا به فرم ساده تر:

1
2
3
4
5
6
7
static void Main(string[] args)
{
    int x = 0;
    Action action = () => Console.WriteLine(x);
    x = 1;
    action();
}

در ابتدا ممکن است فکر کنید عدد صفر نمایش داده می شود، اما با اجرای برنامه خواهید دید جواب 1 است. اما چطور این اتفاق می افتد؟ در واقع سوال این است که متد های بی نام یا عبارات لامبدا چطور هنگام فراخوانی به پارامتر x و حتی مقدار آپدیت شده ی آن دسترسی پیدا می کنند؟

جواب این سوال ها وجود مفهومی است به نام Closure در پس این سینتکس ساده و روان.

Closure چیست؟

به بیان ساده Closure رفرنسی به یک تابع است، در عین حال که به scop ای که در آن تعریف شده است نیز اشاره دارد. بنابراین می توان عبارت های لامبدا و متد های بی نام را یک Closure دانست.

برای درک این تعریف لازم است چگونگی کار Closure ها را بررسی کنیم:

کامپایلر C# هنگام کامپایل برای هر متد بی نام یک کلاس تولید می کند و متغیر های قابل دسترس در آن scop را نیز به عنوان فیلد هایی از آن کلاس در نظر می گیرد، مثلا برای متد بی نام کد مثال اول، کلاسی مشابه زیر تولید می شود:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
[CompilerGenerated]
private sealed class <>c__DisplayClass1
{
   public int x;
 
   public void
b__0()
   {
       Console.WriteLine(this.x);
   }
}
 
private static void Main(string[] args)
{
   <>c__DisplayClass1 class2 = new <>c__DisplayClass1();
   class2.x = 0;
   Action action = new Action(class2.
b__0);
   class2.x = 1;
   action();
}

همانطور که می بینید کلاس تولید شده فیلدی به نام x و متدی با بدنه متد بی نام را دارا می باشد که در تابع Main یک آبجکت از این کلاس ساخته شده و فیلد x مقدار دهی شده است و سپس متد موجود در این کلاس به عنوان متد بی نام به Action اختصاص یافته و در انتها مجددا مقدار x به 1 تغییر داده شده است.

در واقع با این مکانیزم است که یک Anonymous Method به کلیه مقادیر و فیلد ها، به خصوص مقادیر آپدیت شده متغیر ها دسترسی دارد.

بد نیست برای کامل تر شدن بحث مثال دیگری را نیز بررسی کنیم.

کد زیر را ببینید:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
namespace Closure
{
    class Program
    {
        static Action[] actionArr = new Action[10];
 
        static void FillActions(int count)
        {
            for (int i = 0; i < count; i++)
            {
                actionArr[i] = delegate()
                {
                    Console.WriteLine("{0} ", i);
                };
            }
        }
 
        static void Main(string[] args)
        {
            FillActions(actionArr.Length);
 
            for (int i = 0; i < actionArr.Length; i++)
            {
                actionArr[i]();
            }
        }
    }
}

در نگاه اول احتمالا خواهید گفت که عدد 1 تا 10 نمایش داده خواهد شد، اما خروجی به صورت می باشد:

10 10 10 10 10 10 10 10 10 10

همانطور که گفته شد یک Closure رفرنسی به کلیه متغیر ها و خصوصیات قابل مشاهده در آن scop را در خود نگه می دارد. در این مثال حوزه فعالیت متغیر i درون حلقه for بوده و مقدار آن تا 10 افزایش میابد، دقت کنید که scop متغیر i تغییری نمی کند و یا پایان نپذیرفته و متغییر جدیدی ایجاد نمی شود، به همین دلیل است که کلیه متد ها به مقدار نهایی متغییر i اشاره دارند.

فکر می کنم نگاهی به کد تولید شده توسط کامپایلر موضوع را روشن تر کند:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
private static void FillActions(int count)
{
   Action action = null;
   <>c__DisplayClass2 class2 = new <>c__DisplayClass2();
   class2.i = 0;
   while (class2.i < count)
   {
       if (action == null)
       {
           action = new Action(class2.b__0);
       }
       actionArr[class2.i] = action;
       class2.i++;
   }
}

در واقع به دلیل داشتن یک scop ثابت برای متغیر i، تنها یک شی از کلاس <>c__DisplayClass2  ایجاد شده و بین کلیه action ها به اشتراک گذاشته شده است.

حال فرض کنید حلقه for را به صورت زیر تغییر دهیم:

1
2
3
4
5
6
7
8
for (int i = 0; i < count; i++)
{
    int j = i;
    actionArr[i] = delegate()
    {
        Console.WriteLine("{0} ", j);
    };
}

اینبار مشاهده خواهید کرد که اعداد 1 تا 10 نمایش داده می شود، چرا که scop متعیر j در هر بار اجرای حلقه پایان پذیرفته و مجددا ایجاد خواهد شد، بنابراین کامپایلر برای پیاده سازی درست مفهوم Closure که لازم است هر متد به scop خود اشاره کند، کدی مشابه زیر تولید خواهد کرد:

1
2
3
4
5
6
7
8
9
private static void FillActions(int count)
{
   for (int i = 0; i < count; i++)
   {
       <>c__DisplayClass2 class2 = new <>c__DisplayClass2();
       class2.j = i;
       actionArr[i] = new Action(class2.b__0);
   }
}

مشاهده می کنید که در هر حلقه یک شی جدید تولید شده و متغیر j مقدار دهی می شود.

پ.ن: بد نیست بدانید Closure یکی از مباحث پر کاربرد در زبان های functional محسوب می شود و ایده اولیه آن نیز از همین زبان های نشات گرفته شده و برای اولین بار پیاده سازی آن در زبان Scheme انجام شد.

                             


 

Private Diary 7.4 برنامه دفترچه خاطرات شخصی اندروید

Private Diary 7.4 برنامه ایست قابل اعتماد که به کمک آن میتوانید وقایع زندگی خود را ثبت کرده و تمام علایق و احساسات خود را بنویسید. توسط این برنامه کاربران عزبز میتوانند یادداشتها و خاطرات زندگی خود را همراه خود داشته و از طریق پسوورد از آنها محافظت نمایند. این برنامه هم اکنون در مارکت گوگل با قیمت 1.99 دلار به فروش می رسد .

ویژگیهای برنامه Private DIARY:
امکان یادداشت و ذخیره سازی رویدادها
امکان محافظت از یادداشتها از طریق پسووردگذاری
امکان نمایش وقایع در بازه های زمانی
امکان نمایش وقایع بصورت دسته بندی شده
امکان تغییر ظاهر و پوسته
امکان بکاپ گیری از وقایع به SD کارت
امکان بازگردانی بکاپ ها از SD کارت
امکان ضمیمه کردن عکس
امکان اضافه کردن دسته بندی های دلخواه
امکان فرستادن وقایع از طریق ایمیل
امکان تغییر ترتیب رویدادها
پشتیبان گیری خودکار هنگام خروج از برنامه
امکان جستجو در میان وقایع
امکان ارسال وقایع با SMS
امکان آپلود و دانلود وقایع به حساب DropBox
امکان تگ گذاری 
نمایش تقویم و دارا بودن یادآور