آموزش سی شارپ – مفاهیم تکمیلی ارث بری
در این جلسه از مبحث آموزش برنامه نویسی سی شارپ قصد داریم مباحث sealed و Interface و Enumerations را بررسی کنیم.
حتما قبل از بررسی مفاهیم تکمیلی ارث بری نوشته ی مطالب قبلی این دورهی آموزشی را با هشتگ #دوره آموزشی_سی_شارپ در سایت ما را مطالعه کنید.
مفاهیم تکمیلی ارث بری
استفاده از sealed برای جلوگیری از ارثبری
واژهی sealed به معنای مهر و موم شده است و نمیتوان از یک کلاس مهر و موم شده ارثبری کرد. به منظور sealed کردن یک کلاس، کافی است که در ابتدای تعریف کلاس از کلمهی کلیدی sealed استفاده کنید. دقت کنید که یک کلاس را نمیتوان هم بهصورت sealed و هم به صورت abstract تعریف کرد؛ چرا که کلاس abstract به تنهایی کامل نیست. برای این که اجرای کاملی داشته باشد وابسته به کلاسهای مشتق شده از خودش است.
به مثال زیر دقت کنید:
sealed class A
{
}
class B : A
{
// ERROR! Can't derive from class A
}
// Using :
B ob = new B();
در مثال بالا، کلاس A بهصورت sealed تعریف شده است بنابراین هیچ کلاسی نمیتواند از این کلاس ارثبری کند. به این معنا که B نمیتواند از کلاس sealed شدهی A، ارثبری کند. نکتهی دیگر این است که sealed میتواند در virtual methods نیز برای پیشگیری از override شدن مورد استفاده قرار گیرد.
به نمونهی زیر توجه کنید:
class B
{
public virtual void MyMethod() { /* ... */ }
}
class D : B
{
sealed public override void MyMethod() { /* ... */ }
}
class X : D
{
// Error! MyMethod() is sealed!
public override void MyMethod() { /* ... */ }
}
در اینجا، کلاس B یک متد virtual دارد که در کلاس D هم override و هم sealed شده است. از این رو کلاسهایی که از D ارثبری میکنند دیگر نمیتوانند ()MyMethod را override کنند زیرا این متد دیگر sealed شده است.
مفهوم Interfaces
یک interface مجموعهای از متدها را تعریف میکند که توسط یک کلاس اجرا خواهند شد. یک interface هیچ متدی را اجرا نمیکند، از این رو، interface یک سازهی کاملا منطقی است که فقط نشان دهندهی قابلیت و عملکرد است و هیچ قسمت اجرایی ندارد. Interface از نظر syntax متشابه با abstract class است. در interface نیز متدها بدنه ندارند. این به این معنی است که در interface متدها اجرا نمیشوند. Interface مشخص میکند که چه کاری باید انجام شود اما به چگونگی انجام شدن آن اهمیت نمیدهد. هنگامی که یک interface تعریف میشود، هر تعداد کلاس که بخواهید میتوانید این interface را اجرا کنند. همچنین یک class میتواند به تعداد دلخواه interface اجرا کند. Interface توسط کلمهی کلیدی interface تعریف میشود.
در زیر فرم ساده شدهی یک interface را میبینید:
interface name {
ret-type method-name1(param-list);
ret-type method-name2(param-list);
// ...
ret-type method-nameN(param-list);
}
در زیر یک نمونه از interface را میبینید که مشخص کنندهی interface یک class است که یک سری عدد را تولید میکند:
public interface ISeries
{
int GetNext(); // return next number in series
void Reset(); // restart
void SetStart(int x); // set starting value
}
علاوه بر متدها، interface میتواند دارای property ،indexer و event باشد. Interfaceها نمیتوانند data member داشته باشند. آنها همچنین دارای constructor ،destructor و operator methods نیستند. هیچ عضوی نمیتواند در آنها بهصورت static تعریف شود.
اجرای interfaceها
زمانی که یک interface تعریف میشود، یک یا چند کلاس میتوانند این interface را اجرا کنند. برای اجرای یک interface، نام آن را بعد از نام کلاس (به همان طریقی که نام base class را مینوشتید) مینویسید.
فرم کلی کلاسی که یک interface را اجرا میکند به شکل زیر است:
class class-name : interface-name {
// class-body
}
هنگامی که کلاسی میخواهد یک interface را اجرا کند، بایستی به طور کامل آن interface را اجرا کند و نمیتواند فقط بخشی از آن را برای اجرا انتخاب کند. یک کلاس میتواند بیشتر از یک interface را اجرا کند. برای این منظور، پس از نام کلاس، لیست اسامی interfaceهای مورد نظر را توسط کاما از هم جدا میکند. یک کلاس هم میتواند از یک کلاس دیگر ارثبری کند و هم چندین interface را اجرا کند. در این مورد باید نام base class را در ابتدای لیستی که توسط کاما از هم جدا کردهاید، قرار دهید. متدهایی که interface را اجرا میکنند باید public باشند زیرا متدها درون interface بهصورت implicitly public هستند. از این رو اجرای آنها نیز باید public باشد. همچنین return type و signature متدهای اجرایی باید دقیقا با متدهای تعریف شده در interface مطابقت داشته باشد.
مثال:
public interface ISeries
{
int GetNext(); // return next number in series
void Reset(); // restart
void SetStart(int x); // set starting value
}
class ByTwos : ISeries
{
int start;
int val;
public ByTwos()
{
start = 0;
val = 0;
}
public int GetNext()
{
val += 2;
return val;
}
public void Reset()
{
val = start;
}
public void SetStart(int x)
{
start = x;
val = start;
}
}
استفاده از reference variableهای interface
میتوانید interface reference variable بسازید. این چنین متغیری میتواند به هر شیءای که interfaceاش را اجرا میکند، رجوع کند. هنگامی که متد یک شیء را از طریق interface reference صدا میزنید، آن نسخه از متد که شیء مربوط به آن، interface را اجرا کرده است، اجرا میشود. این پروسه شبیه به استفاده از base class reference برای دسترسی به شیء derived class است.
مثال:
public interface ISeries
{
int GetNext();
void Reset();
void SetStart(int x);
}
class ByTwos : ISeries
{
int start;
int val;
public ByTwos()
{
start = 0;
val = 0;
}
public int GetNext()
{
val += 2;
return val;
}
public void Reset()
{
val = start;
}
public void SetStart(int x)
{
start = x;
val = start;
}
}
// Using :
ByTwos twoOb = new ByTwos();
ISeries ob;
ob = twoOb;
ob.SetStart(5);
for (int i = 0; i < 5; i++)
{
Console.WriteLine("Next value is: " + ob.GetNext());
}
بررسی Interface Properties
همانند متدها، properties در interface بدونه بدنه تعریف میشوند.
در زیر فرم کلی تعریف property در interface را میبینید:
type name
{
get;
set;
}
تعریف property در interface مشابه با تعریف auto-implemented property در کلاس است، اما این دو یکی نیستند. این روش تعریف property در interface باعث نمیشود که auto-implement باشد بلکه این تنها مشخص کنندهی نام و نوع property است. همچنین اجازه تغییر access modifier را در قسمت get و set ندارید. به عنوان مثال نمیتوانید set accessor را در interface بهصورت private در نظر بگیرید.
مثال:
public interface ISeries
{
int Next
{
get;
set;
}
}
class ByTwos : ISeries
{
int val;
public ByTwos()
{
val = 0;
}
public int Next
{
get
{
val += 2;
return val;
}
set
{
val = value;
}
}
}
// Using :
ByTwos ob = new ByTwos();
for (int i = 0; i < 5; i++)
Console.WriteLine("Next value is " + ob.Next);
Console.WriteLine("\nStarting at 21");
ob.Next = 21; for (int i = 0; i < 5; i++)
Console.WriteLine("Next value is " + ob.Next);
مفهوم Enumerations
یک enumeration مجموعهای از integerهای نامگذاری شده است. نوع enumeration توسط کلمه کلیدی enum تعریف میشود.
فرم کلی یک enumeration به شکل زیر است:
enum name { enumeration list };
در این جا، نام این enumeration توسط name مشخص شده است و enumeration list لیستی از شناسهها است که توسط کاما از هم جدا شدهاند.
در مثال زیر یک enumeration به اسم Apple وجود دارد که انواع مختلفی از Apple را لیست کرده است:
enum Apple
{
Jonathan, GoldenDel, RedDel, Winesap, Cortland, McIntosh
};
string[] color = {"Red","Yellow",“Blue",”Green","Red","Reddish Green" ;
Apple i;
for (i = Apple.Jonathan; i <= Apple.McIntosh; i++)
Console.WriteLine(i + " has value of " + (int)i);
for (i = Apple.Jonathan; i <= Apple.McIntosh; i++)
Console.WriteLine("Color of " + i + " is " + color[(int)i]);
در ارتباط با “مفاهیم ارث بری سی شارپ” نظرات خودتان را برای ما ارسال کنید تا در تولید دورههای بعدی از نظرات شما استفاده کنیم.
خب این جلسه هم به پایان رسید باید به این نکته توجه کنید که مباحث بالا در برنامه نویسی تجاری اهمیت فراوانی دارد.
برای استفاده از تمامی جلسات از هشتگ #دوره آموزشی_سی_شارپ در سایت ما استفاده کنید.
منتظر جلسهی بعدی دوره آموزشی سی شارپ باشید.



















همیشه از overriding استفاده میکردم ولی این توضیح دربارهی base خیلی دیدم رو باز کرد
موفق باشید.
اگه هم base هم derived متدی به اسم Show داشته باشن، چطور میفهمیم کدوم اجرا میشه؟
اگر هر دو کلاس پایه و مشتق متدی به نام Show داشته باشند، اینکه کدام متد اجرا میشود بستگی به تعریف متد در کلاس پایه دارد؛ اگر متد پایه با کلیدواژه virtual تعریف شده و در کلاس مشتق با override بازنویسی شده باشد، هنگام فراخوانی متد روی شیء از نوع مشتق، نسخهی بازنویسی شده اجرا میشود، اما اگر متد پایه virtual نباشد و متد همنامی در مشتق تعریف شود (که به آن پنهانسازی میگویند)، اجرای متد بستگی به نوع اشارهگر یا متغیر دارد؛ یعنی اگر متغیر از نوع پایه باشد متد پایه و اگر از نوع مشتق باشد متد مشتق اجرا میشود. بنابراین، کلمات کلیدی virtual و override تعیینکننده رفتار اصلی هستند و بدون آنها، نوع متغیر هنگام فراخوانی مشخص میکند کدام متد اجرا شود.
بخش مربوط به مخفیسازی اعضا با new خیلی مفید بود
خوشحالیم این نوشته به شما کمک کرده است.
وقتی base class چند constructor داره، دقیقا کِی باید از base() استفاده کنیم؟
وقتی کلاس پایه چند سازنده (constructor) دارد، باید هر زمان که سازندهی کلاس مشتق نیاز دارد نسخهای خاص از سازندههای پایه را صدا بزند که مناسب مقداردهی اولیه آن باشد، از base() استفاده کنید تا مشخص کنید کدام سازندهی کلاس پایه اجرا شود. اگر سازندهی پایهی بدون پارامتر وجود داشته باشد و شما در سازنده مشتق هیچ فراخوانی base() نداشته باشید، کامپایلر به طور خودکار همان سازنده بدون پارامتر را صدا میزند. اما اگر سازنده بدون پارامتر وجود نداشته باشد یا بخواهید یکی از سازندههای پارامتردار را فراخوانی کنید، باید حتماً به صورت صریح base(arguments) را بنویسید. در واقع، base() زمانی لازم است که بخواهید کنترل دقیقی روی نحوه مقداردهی اولیه بخش پایه داشته باشید یا سازنده پایه پیشفرض وجود نداشته باشد.
همیشه فکر میکردم استفاده از new فقط برای ساختن شیء هست، این کاربردش توی ارثبری برام تازگی داشت
موفق باشید.