در C#، میتوان یک سلسلهمراتب «کلاس» را با «ارثبری» تعریف کرد؛ ارثبری به کلاس مشتق (Car) اجازه میدهد رفتار و دادهی کلاس والدش (Vehicle) را به ارث ببرد. اگر کلاس والدی مشخص نشود، کلاس از کلاس object ارث میبرد.
کلاسهای والد به سه روش میتوانند قابلیتهایی در اختیار کلاسهای مشتق بگذارند:
virtual که مانند یک متد معمولی است، با این تفاوت که کلاسهای مشتق میتوانند آن را تغییر دهند.abstract که متدی بدون پیادهسازی است و کلاسهای مشتق باید آن را پیادهسازی کنند. کلاسی که متدهای abstract دارد باید خودش هم با abstract علامتگذاری شود. از کلاسهای انتزاعی نمیتوان نمونه ساخت.اصلاحکنندهی دسترسی protected اجازه میدهد به عضوی از کلاس والد در یک کلاس مشتق دسترسی داشته باشیم، اما دسترسی از کلاسهای دیگر را مسدود میکند.
کلاسهای مشتق میتوانند از طریق کلیدواژهی base به اعضای کلاس والد دسترسی داشته باشند.
// Inherits from the 'object' class
abstract class Vehicle
{
// Can be overridden
public virtual void Drive()
{
}
// Must be overridden
protected abstract int Speed();
}
class Car : Vehicle
{
public override void Drive()
{
// Override virtual method
// Call parent implementation
base.Drive();
}
protected override int Speed()
{
// Implement abstract method
}
}
سازندهی یک کلاس مشتق بهطور خودکار، پیش از اجرای منطق سازندهی خودش، سازندهی کلاس والد را فراخوانی میکند. میتوان با کلیدواژهی base آرگومانهایی به سازندهی کلاس والد فرستاد:
abstract class Vehicle
{
protected Vehicle(int wheels)
{
Console.WriteLine("Called first");
}
}
class Car : Vehicle
{
public Car() : base(4)
{
Console.WriteLine("Called second");
}
}
وقتی بیش از یک کلاس از یک کلاس والد مشتق شود، آن دو (یا چند) کلاس اغلب نسخههای متفاوتی از یک متد کلاس والد را پیادهسازی میکنند. این یک اصل بسیار مهم است که به آن «چندریختی» میگویند. برای نمونه، در گونهای متفاوت از مثال بالا نشان میدهیم که کد استفادهکننده از Vehicle چگونه بسته به اینکه چه نوع وسیلهی نقلیهای نمونهسازی شده است، رفتار خود را تغییر میدهد.
abstract class Vehicle
{
public abstract string GetDescription();
}
class Car : Vehicle
{
public Car()
{
}
public override string GetDescription()
{
return "Runabout";
}
}
class Rig : Vehicle
{
public Rig()
{
}
public override string GetDescription()
{
return "Big Rig";
}
}
Vehicle v1 = new Car();
Vehicle v2 = new Rig();
v1.GetDescription();
// => Runabout
v2.GetDescription();
// => Big Rig
در این تمرین شما یک بازی نقشآفرینی به اسم «Wizard and Warriors» بازی میکنید که به شما اجازه میدهد در نقش یک جادوگر یا یک جنگجو بازی کنید.
برای جنگجوها و جادوگرها قواعد متفاوتی وجود دارد که تعیین میکند چه مقدار امتیاز آسیب وارد میکنند.
برای یک جنگجو، قواعد به این صورت است:
برای یک جادوگر، قواعد به این صورت است:
به طور کلی، شخصیتها هرگز آسیبپذیر نیستند. اما جادوگرها اگر طلسمی آماده نکرده باشند، آسیبپذیر هستند.
شما شش وظیفه دارید که با شخصیتهای جنگجو و جادوگر کار میکنند.
متد ToString() را در کلاس Character بازنویسی کنید تا توصیفی از شخصیت را برگرداند، با این قالب: "Character is a <CHARACTER_TYPE>".
var warrior = new Warrior();
warrior.ToString();
// => "Character is a Warrior"
مطمئن شوید که متد Character.Vulnerable() همیشه false برمیگرداند.
var warrior = new Warrior();
warrior.Vulnerable();
// => false
متد Wizard.PrepareSpell() را پیادهسازی کنید تا به جادوگر اجازه دهید از قبل طلسمی آماده کند.
var wizard = new Wizard();
wizard.PrepareSpell();
مطمئن شوید که متد Vulnerable() اگر جادوگر طلسمی آماده نکرده باشد true برمیگرداند؛ در غیر این صورت false برگرداند.
var wizard = new Wizard();
wizard.Vulnerable();
// => true
متد Wizard.DamagePoints() را پیادهسازی کنید تا امتیاز آسیب واردشده را برگرداند: ۱۲ امتیاز آسیب وقتی طلسمی آماده شده باشد و ۳ امتیاز آسیب وقتی آماده نشده باشد.
var wizard = new Wizard();
var warrior = new Warrior();
wizard.PrepareSpell();
wizard.DamagePoints(warrior);
// => 12
متد Warrior.DamagePoints() را پیادهسازی کنید تا امتیاز آسیب واردشده را برگرداند: ۱۰ امتیاز آسیب وقتی هدف آسیبپذیر است و ۶ امتیاز آسیب وقتی نیست.
var warrior = new Warrior();
var wizard = new Wizard();
warrior.DamagePoints(wizard);
// => 10