السلام عليكم ورحمة الله وبركاته
قد يكون الموضوع مكرر أو قد يكون هناك بعض الإخوة أو الإخوات ممن تشبعوا بالـ Inheritance في ضل لغات برمجية أخرى مثل C++ أو Java. ولكن صدقوني أنه سيتم التطرق لبعض الأشياء التي قد تكون غائبة عن البعض أو التي لها علاقة بالـ Compiler . سيتم التطرق للمواضيع التالية بشكل موجز وبسيط، فبسم الله نبدأ:
أولا : Inheritance
يمكن لـ Class أن يرث Class آخر وذلك لتمديد أو تخصيص الـ Class الأصل. وراثة Class معين من شأنه إتاحة الفرصة لإستخدام وظائف هذا الـ Class بدل من بنايته من الصفر. لا يمكن لـ Class أن يرث أكثر من Class واحد فقط ولكن يمكن أن يتم وراثة هذا الـ Class من عدة Classes. يمكن لـ Class أن يرث عدة interfaces. دعونا نأخذ المثال التالي :
// This will be our baseclass
public class Vehicle
{
public int MaxSpeed;
public int Passaengers;
}سنقوم بتعريف كلا من Car و Truck حيث سيرث كل منهما Vehicle وذلك من شأنه إعطاء كلا من Car و Truck كل ما هو موجود في Vehicle بالإضافة إلى كل ما هو جديد في كل منهما.
public class Vehicle
{
public int MaxSpeed;
public int Passaengers;
}
public class Car : Vehicle // inherits from Vehicle
{
public bool IsHatchBack;
}
public class Truck : Vehicle // inherits from Vehicle
{
public double MaxLoad;
}
// Main Program
class Program
{
static void Main(string[] args)
{
// New C# 3.0 initialzation of objects
Car carOb = new Car() { MaxSpeed = 160, Passaengers = 4, IsHatchBack = true };
// Old scholl initialization of objects
Truck truckOb = new Truck();
truckOb.MaxSpeed = 140;
truckOb.Passaengers = 2;
truckOb.MaxLoad = 500;
Console.WriteLine("Car");
Console.WriteLine("Maximum Speed: {0} kmph", carOb.MaxSpeed);
Console.WriteLine("Passemgers: {0}", carOb.Passaengers);
Console.WriteLine("Has hatch back: {0}", carOb.IsHatchBack ? "Yes" : "No");
Console.WriteLine(new string('-', 30));
Console.WriteLine("Truck");
Console.WriteLine("Maximum Speed: {0} kmph", truckOb.MaxSpeed);
Console.WriteLine("Passemgers: {0}", truckOb.Passaengers);
Console.WriteLine("Maximum Load: {0:0.00} kg", truckOb.MaxLoad);
}
}نلاحظ أن كلا من الـ Subclasses يرثون خاصيتين من الـ Baseclass وهي MaxSpeed & Paseengers . قد يتم تسمية الـ Subclass بإسم Derived Class وأيضا الـ Baseclass بالإسم Superclass. إلى الآن لا يوجد شيء معقد بل العملية مباشرة جدا.
ثانيا : Polymorphism
الـ Polymorphism أو تعدد الأشكال هي عبارة عن ما يسمى بـ Is A "هو"، أي أن Subclass Reference is a Baseclass reference ولكن العكس غير صحيح. بمعنى آخر، يمكن لـ Reference من نوع Baseclass (Vehicle) أن يشير إلى Object من نوع Subclass (Car or Truck).
Car carOb; // This is a Car reference new Car(); // This is a Car object store somewhere in the heap // Since a Subclass object is a baseclass object (Always true), then a Car is a Vehicle // So the following line will not produce a syntax error Vehicle ob = new Car(); // Always true // But the converse is not true Car ob = new Vehicle() // A syntax error
دعونا نضيف Static Method للـ Main Program حيث أنها تأخذ Parameter من نوع Vehicle ثم تقوم بطباعة السرعة القصوى.
class Program
{
static void Main(string[] args)
{
// New C# 3.0 initialzation of objects
Car carOb = new Car() { MaxSpeed = 160, Passaengers = 4, IsHatchBack = true };
Display(carOb);
}
public static void Display(Vehicle ob)
{
Console.WriteLine("Max Speed: {0}", ob.MaxSpeed);
}
}ماذا لو قمنا بتعديل Display بحيث أنها تقوم بطباعة ما إذا كانت السيارة Hatch Back أو لا.
public static void Display(Vehicle ob)
{
Console.WriteLine("Has Hatch Back: {0}", ob.IsHatchBack ? "Yes" : "No");
}عندم عمل Compile للكود أعلاه سيظهر خطأ وهو عدم وجود تعريف لـ IsHatchBack في Vehicle. على الرغم من أن الـ Object فعليا من نوع Car وهو يحوي تعريف لـ IsHatchBack إلا أن الـ Reference (ob in our case) هو من نوع Vehicle ولا يحوي تعريف لـ IsHatchBack وهذا ما يجعل الـ Compiler يعترض بإظهار رسالة الخطأ. لكي نصل إلى IsHatchBack ينبغي علينا أن نقوم بما يعرف بالـ Downcasting.
ثالثا : Downcasting
هي ما تعرف بعملية إنشاء Subclass Reference عن طريق Baseclass Reference والعملية العكسية لذلك هي Upcasting حيث أنها تحدث بشكل ضمني وذلك وفقا لقاعدة Is A (which is always true). دعونا نأخذ الأمثلة التالية:
class Program
{
static void Main(string[] args)
{
// New C# 3.0 initialzation of objects
Car carOb = new Car() { MaxSpeed = 160, Passaengers = 4, IsHatchBack = true };
Vehicle v = carOb; // OK (Implicit Upcastnig)
Console.WriteLine(v.MaxSpeed);
Console.WriteLine(v.Passaengers);
// Variable 'v' still reference the same 'Car' object as variable 'carOb', if you need profe read on
Console.WriteLine("Is 'v' and 'carOb' reference the same objec ? {0}", v == carOb);
// Downcasting
Car newCarOb = (Car)v;
Console.WriteLine("Has Hatch Back? {0}", newCarOb.IsHatchBack ? "Yes" : "No");
Console.WriteLine(newCarOb == carOb);
Console.WriteLine(newCarOb == v);
}
}في عملية الـ Upcasting يتم التأثير فقط على الـ References وليس الـ Actual Object على عكس عملية الـ Downcasting والتي تتطلب تغيير ظاهر Explicit Casting لأن هناك خطاء محتمل قد يحدث في مرحلة الـ Runtime.
رابعا: The “as” and “is” operators
الـ as Operator تستخدم في عملية الـ Downcasting والتي تعطي null في حالة فشل العملية. أما الـ is Operator فهي تستخدم في التحقق ما إذا كانت عملية الـ Downcasting ستنجح أم لا، بمعنى آخر التحقق من أن object معينة ترث Class معين أو تطبق Interface معينة.
class Program
{
static void Main(string[] args)
{
// as operator
Vehicle v = new Vehicle();
v.MaxSpeed = 160;
Car c = v as Car; // c is null, no exception is thrown.
Car c2 = (Car)v; // this line will throw 'InvalidCastException
// is operator
if (v is Car)
Console.WriteLine(((Car)v).MaxSpeed);
}
}ملاحظة: عندما تستخدم as operator مع متغير من نوع int يقوم الـ compiler بعرض رسالة خطاء، لماذا ؟
خامسا : Virtual Methods Members
عندما يشار إلى method بأنها virtual فهذا من شأنه إعطاء القدره لكل derived class أن يقوم بإعادة تعريف override هذه الـ method. ينطبق ذلك على كل من methods, properties, indexers, and events. دعونا تعديل تعريف كل من Vehicle, Car , and truck.
public class Vehicle
{
public int MaxSpeed;
public int Passaengers;
public virtual string Info() // this method is marked as virtual
{
return string.Format("Maximum Speed: {0} kmph, Passengers: {1}", MaxSpeed, Passaengers);
}
}
public class Car : Vehicle // inherits from Vehicle
{
public bool IsHatchBack;
public override string Info() // overriding base virtual method
{
return string.Format("Maximum Speed: {0} kmph, Passengers: {1}, Hatch Back: {2}",
MaxSpeed, Passaengers, IsHatchBack);
}
}
public class Truck : Vehicle // inherits from Vehicle
{
public double MaxLoad;
public override string Info() // overriding base virtual method
{
return string.Format("Maximum Speed: {0} kmph, Passengers: {1}, Maximum Load: {2:0.00} kg",
MaxSpeed, Passaengers, MaxLoad);
}
}
// -------------------- Main Program
class Program
{
static void Main(string[] args)
{
Vehicle v = new Vehicle() { MaxSpeed = 250, Passaengers = 2 };
Car c = new Car() { MaxSpeed = 180, Passaengers = 4, IsHatchBack = true };
Truck t = new Truck() { MaxSpeed = 160, Passaengers = 2, MaxLoad = 512.215648 };
DisplayInfo(v);
DisplayInfo(c); // Implicit Downcasting
DisplayInfo(t); // Implicit Downcasting
}
public static void DisplayInfo(Vehicle ob) // 'is a' makes your life a lot more easier
{
Console.WriteLine(ob.Info());
}
}سادسا : Abstract Classes and Abstract Members
هي عبارة عن Classes مجردة تحوي بداخلها Abstract Members وهي تشبة الـ Virtual Method ولكن الفرق الوحيد هو أنها لا تحتوي على تعريف، فقط مجرد declaration. لا يمكننا إنشاء object من أي Abstract Class.
public class MyConcreteClass : MyAbstactClass
{
public override string StringProperty
{
get { return "Overriden abstract property"; }
}
public override void VoidMethod()
{
Console.WriteLine("Overriden abstract method");
}
}
// -------------------- Main Program
class Program
{
static void Main(string[] args)
{
MyConcreteClass ob = new MyConcreteClass();
Console.WriteLine(ob.StringProperty);
ob.VoidMethod();
}
}سابعا : Hiding Inherited Members
قد يكون هناك members متطابقين في كل من الـ Baseclass والـ Subclass
public BaseClass {
public int x;
}
public SubClass : BaseClass {
public int x;
}نقول على المتغير x الموجود في SubClass أنه يخفي Hide المتغير x والموجود في BaseClass. عادة يحدث ذلك عن طريق الخطأ ولهذا السبب يقوم الـ compiler بتوليد رسالة تحذيرية ولكنه في الأخير يقوم بحل هذا الغموض (Which x?) عن طريق نوع الـ Reference، بمعنى آخر لو كان الـ Reference من نوع BaseClass فإنه سيتخدم BaseClass.x والعكس صحيح. في النادر قد تريد أن تقوم بعمل hide لأحد الـ Members الموجودة في الـ Baseclass بصورة متعمدة، في هذه الحالة قم بإستخدام الـ new modifier حيث أن الغرض منها هو إخبار الـ Compiler أو من سوف يقوم بإستخدام الـ code من بعدك أن هذا الـ hide ليس عن طريق الخطأ. لاحظ أن لغة C# تقوم بـ overload للكلمة new keyword حيث أن وظيفتها تتغير على حسب المحتوى الذي يحويها.
سؤال : ما الفرق بين override و new ؟ للإجابة على هذا السؤال دعونا نأخذ المثال التالي:
public class BaseClass
{
public virtual void Foo() { Console.WriteLine("BaseClass.Foo()"); }
}
public class Overrider : BaseClass
{
public override void Foo()
{
Console.WriteLine("Overrider.Foo()");
}
}
public class Hider : BaseClass
{
public new void Foo() { Console.WriteLine("Hider.Foo()"); }
}
// -------------------------- Main Program
class Program
{
static void Main(string[] args)
{
Overrider overrider = new Overrider();
BaseClass b1 = overrider;
overrider.Foo(); // Overrider.Foo()
b1.Foo(); // Overrider.Foo()
Hider hider = new Hider();
BaseClass b2 = hider;
hider.Foo(); // Hider.Foo()
b2.Foo(); // BaseClass.Foo() "Since b2 is a reference of type BaseClass"
}
}ثامنا : The base Keyword
وظيفتها تشبه إلى حد كبير وظيفة الـ this keyword وهي تستخدم لغرضين، الأول هو إستدعاء الـ overridden members من خلال الـ Subclass والثاني هو إستددعاء الـ Constructor الخاص بالـ Baseclass من خلال الـ Subclass. نفس المبداء ينطبق على الـ Hidden members.
public class BaseClass
{
public virtual void Foo() { Console.WriteLine("BaseClass.Foo()"); }
}
public class Overrider : BaseClass
{
public override void Foo()
{
base.Foo(); // Will print 'BaseClass.Foo()'
Console.WriteLine("Overrider.Foo()");
}
}تاسعا : Constructors and Inheritance
ينبغي على اللـ SubClass أن يقوم بتعريف Constructor(s) خاصة به حيث أنه لا يتم توريث الـ Baseclass Constructor(s)، لو حاولت compile الكود التالي ستظهر هناك رسالة خطأ (على الرغم من أن الخطأ ليس له علاقة بغياب الـ Constructor الخاص بالـ Subclass)
public class BaseClass
{
int x;
public BaseClass(int x) { this.x = x; }
}
public class SubClass : BaseClass
{
}هنالك طريقتين للتخلص من الخطأ الأولى عن طريق تعريف Constructor في الـ Subclass ثم إستخدام الكلمة المحجوزة base لإستعداء وتمرير قيمة x إلى الـ BaseClass Constructor. والثانية سأتطرق لها في النقطة التالية :
public class BaseClass
{
int x;
public BaseClass(int x) { this.x = x; }
}
public class SubClass : BaseClass
{
public SubClass(int x) : base(x) // Passing this.Constructor argument to base.Constructor
{ }
}ينبغي الإشارة إلى أن ترتيب الإستدعاء في الـ Constructors يبداء من الـ Baseclass ثم الـ Subclass.
عاشرا: Implicit calling of the parameterless base class constructor
بشكل إفتراضي، إذا لم تقم بتعريف constructor للـ class الذي قمت بإنشائة، سيقوم الـ Compiler بتعريف Parameterless constructor لك. وهذا هو سبب المشلكة الفعلي في النقظة السابقة، وعلى ضوء ذلك يمكننا إعادة حلها بالشكل التالي:
public class BaseClass
{
int x;
public BaseClass() { } // Explicit parameterless constructor definition
public BaseClass(int x) { this.x = x; }
}
public class SubClass : BaseClass
{
}أتمنى للجميع التوفيق والنجاح



