استفاده از توابع محلی c# 7

پنجشنبه 31 فروردین 1396

در این مقاله به یکی از ویژگی های نسخه 7 #C خواهیم پرداخت که باعث بالا رفتن خوانایی و پرفورمنس کد های ما میشود. توابع محلی این نوع توابع معمولا با هدف کپسوله سازی عملیات یک متد بزرگ و جلو گیری فراخوانی تصادفی متد های کمکی استفاده میشوند.

استفاده از توابع محلی c# 7

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

آیا c# 7 راه حلی برای این مشکل دارد؟

خوشبختانه جواب این سوال مثبت است c# 7 به شما پیشنهاد میدهد از توابع محلی استفاده کنید.با در نظر گرفتن سناریو پیش رو و با استفاده از دیزاین پترن mvvm ما یک ویو مدل داریم که بخشی از یک نرم افزار کاربردی که با استفاده از پلتفرم یونیورسال ویندوز(uwp) ایجاد شده را میسازد.مدل مورد نظر در نهایت بخشی از پازل خواهد بود که سایر اجزا را به یکدیگر پیوند خواهد داد ،یک متد که مسئولیت جمع آوری و پیش پردازش داده ها را قبل از نمایش دارد.

ویومدل مورد نظر ممکن است کدی شیبه کد زیر داشته باشد.

    class Program
    {
       static IEnumerable<string> YAxis { get; set; }
       static IEnumerable<string> XAxis { get; set; }
       static IEnumerable<double> Data { get; set; }
     
       static void Main(string[] args)
       {
          GetData();
       }
     
       static void GetData()
       {
          YAxis = GetYAxisLabels();
          XAxis = GetXAxisLabels();
          Data = GetDataPoints();
       }
     
       static IEnumerable<string> GetYAxisLabels()
       {
          for(int i = 0; i < 10; i++)
          yield return $"YLabel {i}";
       }
     
       static IEnumerable<string> GetXAxisLabels()
       {
          for (int i = 0; i < 20; i++)
          yield return $"XLabel {i}";
       }
     
       static IEnumerable<double> GetDataPoints()
       {
          Random rand = new Random();
          for (int i = 0; i < 20; i++)
          yield return rand.NextDouble();
       }
    }

توجه :

ما از کنسول اپلیکیشن برای شبیه سازی اینکه این روش چقدر میتواند در نرم افزار های پلتفرم  یونیورسال ویندوز بغرنج باشد استفاده میکنیم اما میتوانید شاهد باشید کلاس مورد نظر چقدر طولانی خواهد شد زمانیکه متد های زیادی تنها توسط یک متد فراخوانی شوند این در صورتی است که ما برای جدا سازی کدها و وظایف ، متد ها را به وجود آوردیم .

اما کلاس فوق در صورتیکه از توابع محلی استفاده شود به چه صورت خواهد بود ؟ نگاهی به قطعه کد زیر بیندازید.

static void GetData()
        {
            YAxis = getYAxisLabels();
            XAxis = getXAxisLabels();
            Data = getDataPoints();

            IEnumerable<string> getYAxisLabels()
            {
                for (int i = 0; i < 10; i++)
                    yield return $"YLabel {i}";
            }

            IEnumerable<string> getXAxisLabels()
            {
                for (int i = 0; i < 10; i++)
                    yield return $"XLabel {i}";
            }

            IEnumerable<double> getDataPoints()
            {
                Random rand = new Random();
                for (int i = 0; i < 10; i++)
                    yield return rand.NextDouble();
            }
        }

با توجه به کد قبلی کدها تغییر نکرده اند. اما ما به سرعت می توانیم توابع محلی را ببینیم که مستقیما درون متد ایجاد شده اند. این یکی از مفاهیم اولیه از طراحی توابع محلی می باشد. و به ما اجازه می دهد که به سرعت کلاس را درک کنیم خصوصا اگر ما نیاز داشته باشیم کلاسی بسازیم که برای دیگران خوانایی بالایی داشته باشد علاوه بر این با استفاده از توابع محلی اطمینان حاصل خواهیم کرد که این توابع به صورت تصادفی در جای دیگری از کلاس فراخوانی نخواهد شد. اگر شما می خواهید نمونه ای از خروجی کد بالا ببینید کافی است کد زیر را به کلاس بیفزایید.

for (int i = 0; i < 10; i++)
            {
                Console.WriteLine(YAxis.ElementAt(i) + " /// " + XAxis.ElementAt(i) + " /// " + Data.ElementAt(i));
            }

خروجی کد بالا باید مشابه عکس زیر باشد.

قطعا متوجه شده اید که فراخوانی توابع محلی قبل از پیاده سازی آنها انجام شده است. چنین ویژگی در توابع محلی جهت تمایز از عبارت های lambda  به وجود آمده است. بلوک کد زیر نمونه ای است که در آن مثالی از تمایز توابع محلی و عبارت lambda آورده شده است.

    static void RecursiveTest()
    {
       Action printMessageDelegate = () => {
          Console.WriteLine("Delegae called");
       };
     
       printMessageDelegate();
     
       printMessageLocalFunction();
     
       void printMessageLocalFunction()
       {
          Console.WriteLine("Local function called");
       }
    }

خروجی کد بالا به صورت زیر است.

شی printMessageDelegate باید قبل از فراخوانی تعریف شده باشد.( همانطور که می بینید این شی با یک عبارت lambda تعریف شده). اما در زیر آن می توانید ببینید که یک تابع محلی قبل از آنکه پیاده سازی شده باشد فراخوانی شده البته تفاوت مهم میان عبارت های lambda و توابع محلی که ممکن است شما را به خود جلب کند کارایی و ضریب پاسخگویی بالاست. زمانیکه شما نیاز به یک کد با ضریب پاسخگویی بالا داشته باشید ویژگی پرفورمنس توابع محلی حائز اهمیت خواهد بود.

آموزش سی شارپ

فایل های ضمیمه

برنامه نویسان

نویسنده 3355 مقاله در برنامه نویسان
  • C#.net
  • 2k بازدید
  • 4 تشکر

کاربرانی که از نویسنده این مقاله تشکر کرده اند

در صورتی که در رابطه با این مقاله سوالی دارید، در تاپیک های انجمن مطرح کنید