توابع محلی، امکان تعریف یک تابع را درون یک متد، فراهم میکنند. هدف آنها تدارک توابعی کمکی است که به سایر قسمتهای کلاس مرتبط نمیشوند. برای مثال اگر متدی نیاز به کار با یک private method دیگر را دارد و این متد خصوصی در جای دیگری استفاده نمیشود، میتوان جهت بالابردن خوانایی برنامه و سهولت یافتن متد مرتبط، این متد خصوصی را تبدیل به یک تابع محلی، درون همان متد کرد.
بازنویسی کدهای C# 6 با توابع محلی C# 7
کلاس زیر را که بر اساس امکانات C# 6 تهیه شدهاست، در نظر بگیرید:
متد خصوصی همین کلاس را توسط Func delegates میتوان به صورت ذیل خلاصه کرد (باز هم بر اساس امکانات C# 6):
به این ترتیب نیاز به تعریف یک متد private دیگر کمتر خواهد شد.
اکنون در C# 7 میتوان این Func delegate را به نحو ذیل تبدیل به یک local function کرد:
مزیت کار با local functions نسبت به Func delegates محلی
در قطعه کد فوق، کار انجام شده صرفا استفادهی از یک Syntax جدید نیست؛ بلکه از لحاظ کارآیی نیز سربار کمتری را به همراه دارد. زمانیکه Func Delegates تعریف میشوند، کار ایجاد یک anonymous type، وهله سازی و فراخوانی آنها توسط کامپایلر صورت میگیرد. اما حین کار با توابع محلی، کامپایلر با یک متد استاندارد سروکار دارد و هیچکدام از مراحل یاد شده و سربارهای آنها رخ نمیدهند (هیچگونه GC allocation ایی نخواهیم داشت). به علاوه اینبار کامپایلر فرصت in-line تعریف کردن متد را به نحو بهتری یافته و به این ترتیب کار سوئیچ بین متدهای مختلف کاهش پیدا میکند که در نهایت سرعت برنامه را افزایش میدهند.
میدان دید توابع محلی
البته با توجه به اینکه متد مثال فوق محلی است، به تمام متغیرها و پارامترهای متد دربرگیرندهی آن نیز دسترسی دارد. بنابراین میتوان پارامتر int age آنرا نیز حذف کرد:
به همین جهت نمیتوانید داخل یک تابع محلی، متغیری را تعریف کنید که همنام یکی از متغیرها یا پارامترهای متد دربرگیرندهی آن باشد.
خلاصه نویسی توابع محلی به کمک expression bodies
میتوان این متد محلی را به صورت یک expression body ارائه شدهی در C# 6 نیز بیان کرد:
روش ارسال یک local function به متدی دیگر
امکان ارسال یک تابع محلی به صورت یک Func delegate به متدی دیگر نیز وجود دارد:
در این مثال GenerateAgeSuffix یک Local function است که به صورت expression body نیز بیان شدهاست. برای ارسال آن به متد OutputSimplePerson، پارامتر دریافتی آن باید به صورت Func تعریف شود.
static void Main(string[] args) { int Add(int a, int b) { return a + b; } Console.WriteLine(Add(3, 4)); }
بازنویسی کدهای C# 6 با توابع محلی C# 7
کلاس زیر را که بر اساس امکانات C# 6 تهیه شدهاست، در نظر بگیرید:
public class PersonWithPrivateMethod { public string Name { get; set; } public int Age { get; set; } public override string ToString() { string ageSuffix = GenerateAgeSuffix(Age); return $"{Name} is {Age} year{ageSuffix} old"; } private string GenerateAgeSuffix(int age) { return age > 1 ? "s" : ""; } }
public class PersonWithLocalFuncDelegate { public string Name { get; set; } public int Age { get; set; } public override string ToString() { Func<int, string> generateAgeSuffix = age => age > 1 ? "s" : ""; return $"{Name} is {Age} year{generateAgeSuffix(Age)} old"; } }
اکنون در C# 7 میتوان این Func delegate را به نحو ذیل تبدیل به یک local function کرد:
public class PersonWithLocalFunction { public string Name { get; set; } public int Age { get; set; } public override string ToString() { return $"{Name} is {Age} year{GenerateAgeSuffix(Age)} old"; // Define a local function: string GenerateAgeSuffix(int age) { return age > 1 ? "s" : ""; } } }
مزیت کار با local functions نسبت به Func delegates محلی
در قطعه کد فوق، کار انجام شده صرفا استفادهی از یک Syntax جدید نیست؛ بلکه از لحاظ کارآیی نیز سربار کمتری را به همراه دارد. زمانیکه Func Delegates تعریف میشوند، کار ایجاد یک anonymous type، وهله سازی و فراخوانی آنها توسط کامپایلر صورت میگیرد. اما حین کار با توابع محلی، کامپایلر با یک متد استاندارد سروکار دارد و هیچکدام از مراحل یاد شده و سربارهای آنها رخ نمیدهند (هیچگونه GC allocation ایی نخواهیم داشت). به علاوه اینبار کامپایلر فرصت in-line تعریف کردن متد را به نحو بهتری یافته و به این ترتیب کار سوئیچ بین متدهای مختلف کاهش پیدا میکند که در نهایت سرعت برنامه را افزایش میدهند.
میدان دید توابع محلی
البته با توجه به اینکه متد مثال فوق محلی است، به تمام متغیرها و پارامترهای متد دربرگیرندهی آن نیز دسترسی دارد. بنابراین میتوان پارامتر int age آنرا نیز حذف کرد:
public class PersonWithLocalFunctionEnclosing { public string Name { get; set; } public int Age { get; set; } public override string ToString() { return $"{Name} is {Age} year{GenerateAgeSuffix()} old"; // Define a local function: string GenerateAgeSuffix() { return Age > 1 ? "s" : ""; } } }
خلاصه نویسی توابع محلی به کمک expression bodies
میتوان این متد محلی را به صورت یک expression body ارائه شدهی در C# 6 نیز بیان کرد:
public class PersonWithLocalFunctionExpressionBodied { public string Name { get; set; } public int Age { get; set; } public override string ToString() { return $"{Name} is {Age} year{GenerateAgeSuffix(Age)} old"; // Define a local function: string GenerateAgeSuffix(int age) => age > 1 ? "s" : ""; } }
روش ارسال یک local function به متدی دیگر
امکان ارسال یک تابع محلی به صورت یک Func delegate به متدی دیگر نیز وجود دارد:
public class LocalFunctionsTest { public void PassAnonFunctionToMethod() { var p = new SimplePerson { Name = "Name1", Age = 42 }; OutputSimplePerson(p, GenerateAgeSuffix); string GenerateAgeSuffix(int age) => age > 1 ? "s" : ""; } private void OutputSimplePerson(SimplePerson person, Func<int, string> suffixFunction) { Output.WriteLine( $"{person.Name} is {person.Age} year{suffixFunction(person.Age)} old"); } }