نکته مهم: این مقاله به مرور زمان، ویرایش و یا تکمیل میشود!
در صورتی که با مشکل تایپی، دستوری و یا مفهومی در این مقاله برخورد کردید، از شما دوست عزیز و گرامی، صمیمانه تقاضا میکنم که اینجانب را مطلع کرده، تا نسبت به تصحیح و تکمیل آن، در اسرع وقت اقدام نمایم.
با کمال تشکر
داریوش تصدیقی
۰۹۱۲۱۰۸۷۴۶۱ - DariushT@GMail.com - https://WebsiteAnalytics.ir
نسخه ۱.۴
با عرض سلام و احترام خدمت شما دوستان خوب و صمیمی، اکثر تعاریف و استانداردهای ذیل، در کلاسهای عمومی و خصوصی اینجانب به صورت کامل و به همراه فلسفه آنها شرح داده میشود. این مجموعه به عنوان یک مرجع در اختیار شما عزیزان قرار خواهد گرفت و امیدوارم که در زمان مناسب، فلسفه و شرح هر یک از موارد ذیل را در همین مقاله و یا در مقالات دیگری بیان نمایم.
از یک دیدگاه، متغیرها به دو دسته تقسیم میشوند:
نمونه: int, long, float, ... و کلیه Struct ها.
نمونه: string
نکته: تمامی متغیرهایی که در Stack قرار میگیرند یا اصطلاحا Allocate میشوند، مطلقا مقدار اولیه ندارند! باید دقت داشته باشیم که حتی نباید بگوییم که مقدار null خواهند داشت!
نکته: تمامی متغیرهایی که در Heap قرار میگیرند یا اصطلاحا Allocate میشوند، اتفاقا مقدار اولیه مشخص دارند.
نمونه: تمامی متغیرهای عددی، مقدار صفر خواهند داشت. تمامی متغیرهای Reference Type، مقدار null خواهند داشت.
نکته: تمامی آرایهها (Array)، از هر جنسی که باشند، Reference Type میباشند.
از دیدگاه دیگری، متغیرها به دو دسته تقسیم میشوند:
نکته: تمامی متغیرهایی که در داخل function یا method تعریف میشوند، در داخل Stack قرار میگیرند (Allocate میشوند).
نکته: تمامی متغیرهایی که در خارج از function یا method تعریف میشوند (در داخل کلاس تعریف میشوند)، در داخل Heap قرار میگیرند (Allocate میشوند).
هر متغیری از هر جنسی را میتوان در داخل متغیری از همان جنس قرار داد، اعم از اینکه جنس آنرا بشناسیم و یا نشناسیم:
int x, y; x = y; y = x;
googooli m, n; m = n; n = m;
ولی هرگاه بخواهیم متغیری از یک جنس را در داخل متغیری از جنس دیگری قرار دهیم، سه حالت امکانپذیر است:
میتوان با خیال راحت:
int x; long y; y = x; // Implicit Casting
میتوان به شرط آنکه مسئولیت آن را بپذیریم:
int x; long y; x = y; // Compile Error! x = (int) y; // Explicit Casting
مطلقا نمیشود! ولی ممکن است که بتوانیم با استفاده از Convert، از یک جنس به جنس دیگری آنرا تبدیل نماییم:
int x; string y; x = y; // Compile Error! y = x; // Compile Error! y = x.ToString(); // Convert! x = System.Convert.ToInt32(y); // Convert!
اصطلاحا به پارامترهای ورودی یک تابع = function = method، امضاء (Signature) گفته میشود.
نکته: در صورتی که تابعی به شکل ذیل تعریف شده باشد:
void SomeFunction(int x, int y)
{
}
این غلط است که بگوییم، Signature این تابع int x, int y میباشد! Signature این تابع int, int میباشد! یعنی گفتن (بیان) نام پارامتر (x, y) در Signature، اشتباه (رایج) میباشد.
یعنی تعریف چند تابع، با نام یکسان، ولی با Signature های متفاوت.
نکته: اینکه دو تابع هم نام بوده و Signature یکسانی داشته باشند و صرفا خروجی آنها متفاوت باشد، به منزله Method Overloading نمیباشد! و Compiler خطا میدهد!
نکته: ولی این امکان وجود دارد که ما Method Overloading داشته باشیم، (یعنی دو تابع، هم نام بوده و Signature های متفاوتی داشته باشند) ولی خروجیهای آنها متفاوت باشد.
سازنده کلاس، تابعی است که مطلقا خروجی نداشته (یعنی حتی نباید در سمت چپ آن عبارت void را نوشت) و نام آن دقیقا با نام کلاس یکسان بوده و در زمان خلق شیء (new) به طور خودکار فرآخوانی میشود.
نکته: به سازندهای که پارامتر ورودی (Signature) نداشته باشد، اصطلاحا Default Constructor گفته میشود.
نکته: از آنجایی که سازنده یک تابع میباشد، لذا ما میتوانیم Constructor Overloading نیز داشته باشیم.
نکته: در صورتی که هیچگونه سازندهای برای کلاس ننوشته باشیم، Compiler به طور خودکار یک Default Constructor برای کلاس ما ایجاد میکند.
تاکتیک حرفهای: هرگاه در سیشارپ، ما چیزی را ننویسیم و Compiler آنرا به طور خودکار نوشته و یا برداشت نماید را به صراحت مینویسیم!
نکته: با عنایت به تاکتیک حرفهای (۱)، در صورتی که Constructor خاصی برای کلاسمان در نظر نداشته باشیم، به صراحت Default Constructor را مینویسیم!
نکته: Snippet مربوط به ایجاد یک Constructor، دستور ctor میباشد.
نکته: در صورتی که هر نوع Constructor ای برای کلاسمان بنویسیم، دیگر Compiler به طور خودکار Default Constructor را نخواهد نوشت!
تاکتیک حرفهای: در صورتی که هیچ یک از field یا property های کلاس، الزامی (Required) نباشند، معمولا صرفا Default Constructor را نوشته و از نوشتن Constructor Overload های دیگر اجتناب میکنیم!
تاکتیک حرفهای: در صورتی که در کلاس ما، به طور مثال، سه فیلد الزامی (Required) وجود داشته باشد، معمولا صرفا یک Constructor نوشته و در امضاء (Signature) آن، هر سه فیلد الزامی را به عنوان پارامترهای ورودی مینویسیم.
نکته: یکی از کاربردهای مهم Constructor، مقداردهی اولیه به فیلد (field) های کلاس میباشد.
در صورتی میتوان با متغیرهای Reference Type کار کرد (یا وَر رفت!) که یا آن New کرده، و یا به یک شیء از قبل New شدهای اشاره کند:
نکته: منظور از کار کردن یا وَر رفتن با یک متغیر Reference Type، یعنی استفاده یا بکارگیری یکی از Member های آن!
در دستورات ذیل، خط (2) ما را با خطای Compile Error مواجه خواهد کرد:
Person p; p.Age = 20; // Compile Error!
دستورات ذیل صحیح میباشند:
Person p; p = new Person(); p.Age = 20;
در دستورات فوق، متغیر p، ایجاد (New) شده است!
دستورات ذیل صحیح میباشند:
Person p1 = new Person(); p1.Age = 20; Person p2 = p1; p2.Age = 30;
در دستورات فوق، متغیر p2، ایجاد (New) نشده! ولی به یک شیء از قبل ایجاد (New) شدهای اشاره میکند!
در صورتی که کلاسی در داخل namespace تعریف شود، صرفا دو Access Modifier دارد:
public internal
در صورتی که کلاسی در داخل کلاس دیگری تعریف شود (Nested Class)، پنج Access Modifier دارد:
public private protected internal protected internal
نکته: در صورتی که در زمان نوشتن یک کلاس، Access Modifier آن را مشخص نکنیم، به طور پیشفرض internal میباشد.
اعضاء (Member های) یک کلاس (Class)، عبارتند از:
field property method event
تاکتیک حرفهای: به هیچ عنوان! از فیلد public استفاده نمیکنیم! بلکه آن را private کرده و متناظر آن، یک Property ایجاد میکنیم. اصطلاحا به اینگونه Property ها، Property های وابسته به فیلد (متناظر) میگویند:
public int Age;
private int age; public int Age { get { return age; } set { age = value; } }
تاکتیک حرفهای: به هیچ عنوان! get یا set یک Property متناظر با یک فیلد (Field) را حذف نمیکنیم! بلکه عنداللزوم (در صورت نیاز) get یا set آن را ضعیف (private or protected) میکنیم:
دستورات ذیل مطلقا توصیه نمیگردد:
private int salary; public int Salary { get { return age; } }
به جای دستورات فوق، دستورات ذیل توصیه میگردد:
private int salary; public int Salary { get { return salary; } protected set { salary = value; } }
تاکتیک حرفهای: هر چند که در داخل کلاس (Class) میتوانیم به فیلد (Field) های private مستقیما دسترسی داشته باشیم، و برای خواندن و نوشتن بر روی آنها، مستقیما از خودشان استفاده نماییم، ولی به عنوان یک حرفهای مطلقا! از خود فیلد (Field) ها استفاده نمیکنیم! بلکه از Property متناظر آنها (برای خواندن و نوشتن) استفاده میکنیم.
تذکر خیلی مهم: هیچگاه از یک Property، در درون همان Property استفاده نمیکنیم! اگر چنین شرایطی بوجود آید، دچار خطای Stack Overflow خواهیم شد!
private int age; public int Age { get { return Age; } set { Age = value; } }
دستورات ذیل توصیه نمیگردد:
public void SomeFunction(int age) { this.age = age; }
به جای دستورات فوق، دستورات ذیل توصیه میگردد:
public void SomeFunction(int age) { Age = age; }
به طور کلی، Property ها به دو دسته تقسیم میشوند:
زمانی که میگوییم کلاس C2 از کلاس C1 ارثبری کرده است و یا Inherit شده است.
از نظر نمادهای UML، وراثت را به شکل ذیل نمایش میدهیم:
و از نظر کدهای سیشارپ، به شکل ذیل مینویسیم:
class C2 : C1
و از نظر مفهومی، میگوییم:
C2 is a (an) C1
یعنی کلاس C2 همان کلاس C1 است.
در رابطه با وراثت، رابطه ترایایی، تراگذری و یا تعدی داریم.
معنی رابطه ترایایی:
A < B & B < C => A < C
یعنی اگر کلاس C3 از کلاس C2 ارث برده باشد و نیز کلاس C2 از کلاس C1 ارث برده باشد، میتوان نتیجه گرفت که کلاس C3 از کلاس C1 نیز ارث برده است.
از نظر مفهمومی، اگر کلاس C3 همان کلاس C2 بوده و کلاس C2 نیز همان کلاس C1 باشد، میتوان نتیجه گرفت که کلاس C3 همان کلاس C1 است.
در زبان برنامهنویسی ++C، هر کلاسی میتواند از یک یا چند کلاس Inherit شود، ولی بر خلاف زبان ++C، و همانند زبان جاوا، در زبان برنامهنویسی #C، هر کلاس از یک و فقط یک کلاس Inherit میشود. یعنی در زبانهای برنامهنویسی Java و #C، ما مفهومی به نام Multiple Inheritance نداریم!
در زبان برنامهنویسی #C، کلاسی داریم که به کلاس حضرت آدم، معروف است! یعنی کلاسی که از هیچ کلاسی Inherit نشده است. این کلاس، کلاس object یا System.Object میباشد. که البته مایکروسافت توصیه میکند که در هنگام نگارش از object به جای System.Object استفاده شود.
به غیر از کلاس object، تمام کلاسهایی که مایکروسافت، شرکتها و یا اشخاص دیگر و یا ما تولید میکنیم، از یک و فقط یک کلاس Inherit میشوند!
اگر کلاسی تعریف نماییم، و نگوییم که این کلاس، از چه کلاسی Inherit شده است، به طور خودکار از کلاس object ارثبری میکند.
پس اگر ما کلاسی به شکل ذیل داشته باشیم:
class Person { }
در واقع این کلاس، به صورت ذیل تعریف میشود:
class Person : object { }
و با توجه به اصلی که بارها گفته شده است، که اگر چیزی را ننویسیم، کامپایلر مینویسد و یا برداشت میکند، باید آن را به صراحت بنویسیم، لذا در صورتی که کلاسی تعریف میکنیم و نمیخواهیم از کلاس خاصی Inherit شود، باید به صراحت کلاس object را بنویسیم.
با توجه به توضیحات و توصیفات فوق میتوان به جمله ذیل رسید:
Every thing in C# is an Object!
تمام Member های کلاس پدر (بلااستثناء) به کلاس فرزند منتقل میشود، ولی ممکن است که تحت شرایطی به آنها دسترسی نداشته باشیم! باید دقت داشته باشیم که عدم دسترسی به معنای عدم وجود نیست!
نمیتوانیم Access Modifier های Member های کلاس پدر را در کلاس فرزند تغییر دهیم!
پایان