آموزش: آموزش برنامه نویسی شئ گرا در زبان سی شارپ - قسمت بیستم- کلاس های static و partial و Extension Method ها
چهارشنبه, ۱۳ ارديبهشت ۱۳۹۶، ۱۰:۵۶ ب.ظ
در قسمتی که در مورد کلاس ها و اشیاء صحبت کردیم، گفتیم زمانی که شما کلاسی
را تعریف می کنید باید از روی آن کلاس شئ ای بسازید تا به اعضای آن دسترسی
داشته باشید. اما حالت هایی وجود دارد که شما می توانید یک کلاس و اعضای
آن را به صورتی تعریف کنید که دسترسی به اعضای آن کلاس بدون تعریف شئ از آن
امکان پذیر باشد. در طول این دوره آموزشی با یکی از این کلاس ها کار کرده
ایم. کلاس Console که شما می توانستید بدون ساختن شئ از روی آن متدهای آن
را صدا بزنید. برای مثال متد WriteLine:
Console.WriteLine("Welcome to ITPro.ir");
دلیل این امر، تعریف کلاس Console و اعضای آن به صورت static است.
کلاس ها و اعضای static
اعضای static، در حقیقت اعضایی هستند که وابسته به شئ نیستند و در بین کل شئ های ساخته شده از یک کلاس مشترک می باشند. افرادی که قبلاً با زبان Visual Basic کار کرده باشند با این اعضاء با نام نام Shared آشنای دارند. در زبان سی شارپ اعضای static با کلمه کلیدی static تعریف می شوند. مثال زیر را در نظر بگیرید:
public class Messages { public static void Welcome() { Console.WriteLine("Welcome to ITPro.ir"); } }
در کلاس بالا، متد Welcome به صورت static تعریف شده است. کافیست برای استفاده از این متد به صورت زیر عمل کنیم:
Messages.Welcome();
دقت کنید که هیچ شئ ای از کلاس Messages ایجاد نشده است و تنها با نوشتن نام کلاس و تایپ کاراکتر . به اعضای static آن کلاس دسترسی داریم. در صورتی که شئ ای از کلاس Messages بسازیم و لیست اعضای آن را مشاهده کنیم خبری از متد Welcome نخواهد بود.
ما می توانیم یک کلاس را به صورت static تعریف کنیم. در صورتی که کلاسی به صورت static تعریف شود، تمامی اعضای آن باید به صورت static تعریف شوند و همچنین دیگر امکان ساخت شئ از روی آن کلاس وجود نخواهد داشت:
public static class Messages { public static void Welcome() { Console.WriteLine("Welcome to ITPro.ir"); } }
در صورتی که داخل یک کلاس static عضو غیر static تعریف کنیم، با پیغام خطا مواجه خواهیم شد:
در بخشی که متدها را توضیح می دادیم، هنگام تعریف متدها گفتیم متدهایی که داخل کلاس Program تعریف می شدند را باید به صورت static تعریف کنیم که از داخل متد Main به آنها دسترسی داشته باشیم. دلیل این کار این بود که متد Main خود به صورت static تعریف شده است و شما از داخل یک متد static می توانید تنها متدهای static داخل همان کلاس را صدا بزنید، در غیر اینصورت باید از روی آن کلاس شئ ای بسازید و سپس متد مربوطه را اجرا کنید. به مثال زیر توجه کنید:
public class Messages { public static void Welcome() { Console.WriteLine("Welcome to ITPro.ir"); } public void Goodbye() { Console.WriteLine("Goodbye. Please comeback soon."); } }
کلاس Messages را از حالت static خارج کردیم، پس می توانیم اعضای غیر static داخل آن داشته باشیم. اما فرض کنید می خواهیم از متد Goodbye داخل متد Welcome استفاده کنیم. اگر متد Goodbye را بدون ساختن شئ صدا کنیم پیغام خطا دریافت خواهیم کرد:
برای رفع این مشکل متد Welcome را به صورت زیر باید بنویسیم:
public static void Welcome() { Console.WriteLine("Welcome to ITPro.ir"); var messages = new Messages(); messages.Goodbye(); }
ابتدا از خود کلاس Messages یک شئ ایجاد کرده و از روی شئ اقدام به فراخوانی متد Goodbye کردیم. به این نکته دقت کنید که امکان استفاده از اعضای static در بخش های غیر static مشکلی ایجاد نمی کند و می توان به صورت مستقیم آن ها را فراخوانی کرد.
public class Messages { public static void Welcome() { Console.WriteLine("Welcome to ITPro.ir"); } public void Goodbye() { Console.WriteLine("Goodbye. Please comeback soon."); Welcome(); } }
در متد Goodbye ما به صورت مستقیم متد Welcome را صدا زدیم بدون اینکه پیغام خطایی دریافت کنیم. اما اگر برای فراخوانی متد Welcome در مثال بالا، از کلمه کلیدی this استفاده می کردیم، با پیغام خطا مواجه می شدیم. به این خاطر که کلمه کلیدی this همانطور که در قسمت های قبلی گفتیم، به شئ ساخته شده از روی یک کلاس اشاره می کند.
در ابتدای همین بخش، گفتیم که اعضای static بین کل اشیاء ساخته شده از یک کلاس مشترک هستند. یعنی به ازای هر شئ، مقدار متفاوتی ندارند، چون وابسته به شئ نیستند. برای مثال، کد زیر یک خاصیت static از نوع int با نام Instances تعریف می کند که تعداد اشیاء ایجاد شده داخل یک کلاس را به ما نمایش می دهد. برای اینکه به ازای ساخته شدن هر شئ مقدار Instances یک واحد اضافه شود، سازنده پیش فرض را برای کلاس به صورت زیر می نویسیم:
public class Sample { public static int Instances { get; private set; } public Sample() { Instances++; } }
دقت کنید، بخش set خاصیت Instances با سطح دسترسی private تعریف شده است. یعنی ما تنها می توانیم داخل کلاس آن را مقدار دهی کنیم و خارج از کلاس امکان مقدار دهی به آن وجود ندارد. سپس داخل سازنده پیش فرض یک واحد به Instances اضافه می کنیم. حال کد زیر را اجرا می کنیم:
Console.WriteLine(Sample.Instances); var s1 = new Sample(); Console.WriteLine(Sample.Instances); var s2 = new Sample(); var s3 = new Sample(); Console.WriteLine(Sample.Instances); Console.ReadKey();
با اجرای قطعه کد بالا در متد Main، به ترتیب عددهای 0، سپس 1 و در انتها 3 که به ترتیب تعداد اشیاء ساخته شده از روی کلاس Sample هستند نمایش داده می شود. به همین دلیل گفته می شود که اعضای static بین کلیه اشیاء ساخته شده در یک کلاس مشترک هستند.
سازنده های static
در قسمت قبل گفتیم که سازنده ها به ما امکان اجرای کد مورد نظر در هنگام ساختن اشیاء را می دهند. اما سازنده ای وجود دارد که کاربردش برای اعضای static است. سازنده های static به صورت زیر تعریف می شوند:
static {classname}() { }
که به جای classname نام کلاسی که سازنده برای آن ایجاد می شود را می نویسیم. مثال:
public class StaticConstructor { public static string FirstName { get; set; } public static string LastName { get; set; } static StaticConstructor() { FirstName = "None"; LastName = "None"; } }
در کد بالا، هنگام اجرای سازنده مقادیر FirstName و LastName به None تغییر می کند. اما سازنده static چه زمانی اجرا می شود؟ اجرای سازنده های static درست زمانی که شما تصمیم دارید به یکی از فیلدهای static یک کلاس دسترسی پیدا کنید، تنها برای یکبار اجرا می شود. تنها برای یکبار، یعنی شما اگر 10 مرتبه فیلدهای static یک کلاس را استفاده کنید، سازنده static تنها برای دسترسی اول اجرا خواهد شد. مثال:
Console.WriteLine(StaticConstructor.FirstName); StaticConstructor.FirstName = "Hossein"; Console.WriteLine(StaticConstructor.FirstName);
کد بالا به ترتیب مقادیر None و سپس Hossein را در خروجی چاپ می کند. به نکات زیر هنگام استفاده از سازنده های static توجه کنید:
- سازنده های static تنها یکبار در طول اجرای کد و آن هم زمان اولین دسترسی به اعضای static اجرا خواهند شد.
- سازنده های static نمی توانند سطح دسترسی غیر از public داشته باشند.
- سازنده های static نمی توانند پارامتری را به عنوان ورودی بگیرند.
استفاده از کلاس ها و اعضاء static باید با دقت زیاد انجام شود. زیرا این فیلدها خطرات زیادی را برای برنامه ایجاد می کنند، مخصوصاً برنامه های تحت وب که شما باید مباحث مربوط به همزمانی را در هنگام دسترسی به اعضای static رعایت کنید. در قسمت همزمانی و آشنایی با برنامه نویسی Aynchronous به صورت کامل با این مبحث آشنا می شوید.
Extension Method ها
بعد از آشنایی با کلاس ها و اعضای static به سراغ Exntesion Method ها می رویم. موقعیتی را در نظر بگیرید که می خواهید به یک کلاس متدی اضافه کنید. اما یا کد کلاس در اختیار شما نیست و یا نمی خواهید کد اصلی کلاس دستکاری شود. برای اینکار از Extension Method ها استفاده می شود. این قابلیت به ما این اجازه را می دهد تا به نمونه های یک کلاس رفتاری را اضافه کنیم. این عملیات برای کلاس های تعریف شده توسط خود ما و همچنین نوع های داده اولیه و کلیه کلاس هایی که داخل دات نت تعریف شده اند قابل استفاده می باشد. شیوه کلی تعریف Extension Method ها به صورت زیر است:
1. ابتدا باید یک کلاس static برای تعریف Extension Method ها تعریف کنیم.
2. به ازای هر Extension Method، متدی با ساختار زیر داخل کلاس static تعریف شده ایجاد می کنیم:
public static {return-type} {name}(this {datatype} {instancename}, {parameters}) { }
- return-type نوع داده بازگشتی متد را مشخص می کند.
- name نام متدی که قرار است به شئ مورد نظر از یک کلاس اضافه شود. این نام کاملاً دلخواه است.
- datatype یا نوع داده ای که تصمیم داریم به شئ های آن یک متد اضافه کنیم.
- instancename یا نام متغیری که بواسطه آن می خواهیم به شئ ای که متد بر روی آن ایجاد می شود دسترسی داشته باشیم را مشخص می کنیم.این نام کاملاً دلخواه است.
- parameters یا لیست پارامترهای ورودی متدی که قصد تعریف آن را داریم مشخص می کند.
برای مثال، فرض کنید میخواهیم به نوع داده int یک متد اضافه کنیم که یک عدد را به عنوان ورودی گرفته و عدد داخل متغیر ایجاد شده را به توان ورودی رسانده و بر می گرداند. برای این کار کافیست ابتدا کلاسی برای Extension Method ها اضافه کنیم:
public static class IntExtensions { }
بهتر است برای Extension Method های هر نوع داده، یک کلاس جداگانه ایجاد کنیم مانند LongExtensions یا CustomExtensions که بخش اول اشاره به کلاسی می کند که ما تصمیم داریم برای آن Extension Method ایجاد کنیم. در ادامه متدی با نام Pow به نوع داده Int اضافه می کنیم:
public static class IntExtensions { public static int Pow(this int number, int pow) { int result = 1; for (int counter = 0; counter < pow; counter++) result = result*number; return result; } }
حال برای استفاده از این متد کافیست آن را برای متغیرهایی از نوع int به صورت زیر فراخوانی کنیم:
int myNum = 4; var pow = myNum.Pow(3); Console.WriteLine(pow);
به قسمت تعریف Extension Method بر گردیم، پارامتری با نام number داخل متد Pow تعریف کردیم، در حقیقت این متد به مقدار داخل متغیری اشاره می کند که ما Extension Method را بر روی آن اجرا می کنیم. در کد بالا، پارامتر number به مقدار 4 که داخل متغیر myNum ریخته شده اشاره می کند.
شما برای هر نوع داده و هر کلاسی می توانید Extension متد تعریف کنید. زمانی که Intellisense باز می شود، متد های عادی به صورت یک مکعب نمایش داده می شوند، اما Extension Method ها به صورت یک مکعب که یک فلش آبی رنگ به سمت پایین در بالای آن قرار دارد نمایش داده می شوند.
کلاس های partial
کلاس های partial به شما این امکان را می دهند تا یک کلاس را به چند فایل بشکنید. برای مثال، یک فایل تعریف Property ها و یک فایل تعریف Method ها. در مثال زیر من کلاسی با نام Customer تعریف کردم که این کلاس به سه بخش تقسیم شده است، بخش اول خصوصیات، بخش دوم سازنده ها و بخش سوم متدها:
public partial class Customer { public string FirstName { get; set; } public string LastName { get; set; } public long Age { get; set; } } public partial class Customer { public Customer() { } public Customer(string firstName, string lastName, long age) { FirstName = firstName; LastName = lastName; Age = age; } } public partial class Customer { public string DisplayFullName() { return this.FirstName + " " + this.LastName; } }
در حقیقت Customer یک کلاس است که به سه قسمت تقسیم شده. شما می توانید برای هر قسمت، یک فایل جداگانه ایجاد کنید. کلاس های partial زمان نوشتن برنامه ها کاربرد زیادی ندارند، به شخصه یاد ندارم داخل پروژه ای از این قابلیت استفاده کرده باشم. بیشترین استفاده ای که از کلاس های partial شده است داخل برنامه هایی از نوع Windows Application که فایل های مربوط به فرم های برنامه به چند بخش شکسته شده اند. در قسمت های بعدی با این نوع از برنامه ها بیشتر آشنا خواهیم شد.
بعد از آشنایی با کلاس های static و کلاس های partial و همچنین Extension Method ها، در قسمت بعدی با مبحث Reference Type ها و Value Type ها و همچنین struct ها آشنا خواهیم شد. تا مبحث بعدی شما دوستان عزیز را به خدا می سپارم.
منبع:programming.itpro.ir
- ۹۶/۰۲/۱۳